Fuel Docs Hub

This mdbook contains documentation from multiple repositories in the Fuel ecosystem.

Included Repositories

  • Sway Language
  • Sway Libraries
  • Sway Standards
  • Sway by Example
  • Migrations and Disclosures
  • Verified Addresses
  • Fuel Token Overview
  • Fuel Book
  • Integration Docs
  • Node Operator
  • Fuels-rs (Rust SDK)
  • Specs
  • Guides
  • Introduction
  • Contributing

Introduction

To get started with Forc and Sway smart contract development, install the Fuel toolchain and Fuel full node and set up your first project.

Getting Started

Installing the Fuel toolchain

Please visit the Fuel Installation Guide to install the Fuel toolchain binaries and prerequisites.

Sway Quickstart

Check out the Developer Quickstart Guide for a step-by-step guide on building a fullstack dapp on Fuel. The guide will walk you through writing a smart contract, setting up a wallet, and building a frontend to interact with your contract.

The Fuel Toolchain

The Fuel toolchain consists of several components.

Forc (forc)

The "Fuel Orchestrator" Forc is our equivalent of Rust's Cargo. It is the primary entry point for creating, building, testing, and deploying Sway projects.

Sway Language Server (forc-lsp)

The Sway Language Server forc-lsp is provided to expose features to IDEs. Installation instructions.

Sway Formatter (forc-fmt)

A canonical formatter is provided with forc-fmt. Installation instructions. It can be run manually with

forc fmt

The Visual Studio Code plugin will automatically format Sway files with forc-fmt on save, though you might have to explicitly set the Sway plugin as the default formatter, like this:

"[sway]": {
  "editor.defaultFormatter": "FuelLabs.sway-vscode-plugin"
}

Fuel Core (fuel-core)

An implementation of the Fuel protocol, Fuel Core, is provided together with the Sway toolchain to form the Fuel toolchain. The Rust SDK will automatically start and stop an instance of the node during tests, so there is no need to manually run a node unless using Forc directly without the SDK.

A Forc Project

To initialize a new project with Forc, use forc new:

forc new my-fuel-project

Here is the project that Forc has initialized:

$ cd my-fuel-project
$ tree .
├── Forc.toml
└── src
    └── main.sw

Forc.toml is the manifest file (similar to Cargo.toml for Cargo or package.json for Node), and defines project metadata such as the project name and dependencies.

For additional information on dependency management, see: here.

[project]
authors = ["User"]
entry = "main.sw"
license = "Apache-2.0"
name = "my-fuel-project"

[dependencies]

Here are the contents of the only Sway file in the project, and the main entry point, src/main.sw:

contract;

abi MyContract {
    fn test_function() -> bool;
}

impl MyContract for Contract {
    fn test_function() -> bool {
        true
    }
}

The project is a contract, one of four different project types. For additional information on different project types, see here.

We now compile our project with forc build, passing the flag --asm final to view the generated assembly:

$ forc build --asm final
...
.program:
ji   i4
noop
DATA_SECTION_OFFSET[0..32]
DATA_SECTION_OFFSET[32..64]
lw   $ds $is 1
add  $$ds $$ds $is
lw   $r0 $fp i73              ; load input function selector
lw   $r1 data_0               ; load fn selector for comparison
eq   $r2 $r0 $r1              ; function selector comparison
jnzi $r2 i12                  ; jump to selected function
movi $$tmp i123               ; special code for mismatched selector
rvrt $$tmp                    ; revert if no selectors matched
ret  $one
.data:
data_0 .word 559005003

  Compiled contract "my-fuel-project".
  Bytecode size is 60 bytes.

Standard Library

Similar to Rust, Sway comes with its own standard library.

The Sway Standard Library is the foundation of portable Sway software, a set of minimal shared abstractions for the broader Sway ecosystem. It offers core types, like Result<T, E> and Option<T>, library-defined operations on language primitives, native asset management, blockchain contextual operations, access control, storage management, and support for types from other VMs, among many other things.

The entire Sway standard library is a Forc project called std, and is available directly here. Navigate to the appropriate tagged release if the latest master is not compatible. You can find the latest std documentation here.

Using the Standard Library

The standard library is made implicitly available to all Forc projects created using forc new. In other words, it is not required to manually specify std as an explicit dependency. Forc will automatically use the version of std that matches its version.

Importing items from the standard library can be done using the use keyword, just as importing items from any Sway project. For example:

use std::storage::storage_vec::*;

This imports the StorageVec type into the current namespace.

Standard Library Prelude

Sway comes with a variety of things in its standard library. However, if you had to manually import every single thing that you used, it would be very verbose. But importing a lot of things that a program never uses isn't good either. A balance needs to be struck.

The prelude is the list of things that Sway automatically imports into every Sway program. It's kept as small as possible, and is focused on things which are used in almost every single Sway program.

The current version of the prelude lives in std::prelude, and re-exports the following:

Sway Standards

Just like many other smart contract languages, usage standards have been developed to enable cross compatibility between smart contracts.

For more information on using a Sway Standard, please refer to the Sway-Standards Repository.

Standards

Native Asset Standards

Predicate Standards

Access Control Standards

Contract Standards

Bridge Standards

Documentation Standards

Standards Support

Libraries have also been developed to support Sway Standards. These can be in Sway-Libs.

Example

Some basic example contracts to see how Sway and Forc work.

Additional examples can be found in the Sway Applications repository.

Counter

The following is a simple example of a contract which implements a counter. Both the initialize_counter() and increment_counter() ABI methods return the currently set value.

forc template --template-name counter my_counter_project
```sway\ncontract;

abi TestContract {
    #[storage(write)]
    fn initialize_counter(value: u64) -> u64;

    #[storage(read, write)]
    fn increment_counter(amount: u64) -> u64;
}

storage {
    counter: u64 = 0,
}

impl TestContract for Contract {
    #[storage(write)]
    fn initialize_counter(value: u64) -> u64 {
        storage.counter.write(value);
        value
    }

    #[storage(read, write)]
    fn increment_counter(amount: u64) -> u64 {
        let incremented = storage.counter.read() + amount;
        storage.counter.write(incremented);
        incremented
    }
}\n```

Build and deploy

The following commands can be used to build and deploy the contract. For a detailed tutorial, refer to Building and Deploying.

# Build the contract
forc build

# Deploy the contract
forc deploy --testnet

FizzBuzz

This example is not the traditional FizzBuzz; instead it is the smart contract version! A script can call the fizzbuzz ABI method of this contract with some u64 value and receive back the result as an enum.

The format for custom structs and enums such as FizzBuzzResult will be automatically included in the ABI JSON so that off-chain code can handle the encoded form of the returned data.

```sway\ncontract;

enum FizzBuzzResult {
    Fizz: (),
    Buzz: (),
    FizzBuzz: (),
    Other: u64,
}

abi FizzBuzz {
    fn fizzbuzz(input: u64) -> FizzBuzzResult;
}

impl FizzBuzz for Contract {
    fn fizzbuzz(input: u64) -> FizzBuzzResult {
        if input % 15 == 0 {
            FizzBuzzResult::FizzBuzz
        } else if input % 3 == 0 {
            FizzBuzzResult::Fizz
        } else if input % 5 == 0 {
            FizzBuzzResult::Buzz
        } else {
            FizzBuzzResult::Other(input)
        }
    }
}\n```

Wallet Smart Contract

The ABI declaration is a separate project from your ABI implementation. The project structure for the code should be organized as follows with the wallet_abi treated as an external library:

.
├── wallet_abi
│   ├── Forc.toml
│   └── src
│       └── main.sw
└── wallet_smart_contract
    ├── Forc.toml
    └── src
        └── main.sw

It's also important to specify the source of the dependency within the project's Forc.toml file when using external libraries. Inside the wallet_smart_contract project, it requires a declaration like this:

[dependencies]
wallet_abi = { path = "../wallet_abi/" }

ABI Declaration

```sway\n// ANCHOR: abi_library
library;

// ANCHOR: abi
abi Wallet {
    // ANCHOR: receive_funds
    #[storage(read, write), payable]
    fn receive_funds();
    // ANCHOR_END: receive_funds

    // ANCHOR: send_funds
    #[storage(read, write)]
    fn send_funds(amount_to_send: u64, recipient_address: Address);
    // ANCHOR_END: send_funds
}
// ANCHOR: abi
// ANCHOR_END: abi_library\n```

ABI Implementation

```sway\n// ANCHOR: full_wallet
contract;

use std::{asset::transfer, call_frames::msg_asset_id, context::msg_amount};

// ANCHOR: abi_import
use wallet_abi::Wallet;
// ANCHOR_END: abi_import
const OWNER_ADDRESS = Address::from(0x8900c5bec4ca97d4febf9ceb4754a60d782abbf3cd815836c1872116f203f861);

storage {
    balance: u64 = 0,
}

// ANCHOR: abi_impl
impl Wallet for Contract {
    #[storage(read, write), payable]
    fn receive_funds() {
        if msg_asset_id() == AssetId::base() {
            // If we received the base asset then keep track of the balance.
            // Otherwise, we're receiving other native assets and don't care
            // about our balance of coins.
            storage.balance.write(storage.balance.read() + msg_amount());
        }
    }

    #[storage(read, write)]
    fn send_funds(amount_to_send: u64, recipient_address: Address) {
        let sender = msg_sender().unwrap();
        match sender {
            Identity::Address(addr) => assert(addr == OWNER_ADDRESS),
            _ => revert(0),
        };

        let current_balance = storage.balance.read();
        assert(current_balance >= amount_to_send);

        storage.balance.write(current_balance - amount_to_send);

        // Note: `transfer()` is not a call and thus not an
        // interaction. Regardless, this code conforms to
        // checks-effects-interactions to avoid re-entrancy.
        transfer(
            Identity::Address(recipient_address),
            AssetId::base(),
            amount_to_send,
        );
    }
}
// ANCHOR_END: abi_impl
// ANCHOR_END: full_wallet\n```

Liquidity Pool Example

All contracts in Fuel can mint and burn their own native asset. Contracts can also receive and transfer any native asset including their own. Internal balances of all native assets pushed through calls or minted by the contract are tracked by the FuelVM and can be queried at any point using the balance_of function from the std library. Therefore, there is no need for any manual accounting of the contract's balances using persistent storage.

The std library provides handy methods for accessing Fuel's native asset operations.

In this example, we show a basic liquidity pool contract minting its own native asset LP asset.

```sway\ncontract;

use std::{
    asset::{
        mint_to,
        transfer,
    },
    call_frames::msg_asset_id,
    constants::DEFAULT_SUB_ID,
    context::msg_amount,
    hash::*,
};

abi LiquidityPool {
    fn deposit(recipient: Address);
    fn withdraw(recipient: Address);
}

const BASE_ASSET: AssetId = AssetId::from(0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c);

impl LiquidityPool for Contract {
    fn deposit(recipient: Address) {
        assert(msg_asset_id() == BASE_ASSET);
        assert(msg_amount() > 0);

        // Mint two times the amount.
        let amount_to_mint = msg_amount() * 2;

        // Mint some LP assets based upon the amount of the base asset.
        mint_to(Identity::Address(recipient), DEFAULT_SUB_ID, amount_to_mint);
    }

    fn withdraw(recipient: Address) {
        let asset_id = AssetId::default();
        assert(msg_asset_id() == asset_id);
        assert(msg_amount() > 0);

        // Amount to withdraw.
        let amount_to_transfer = msg_amount() / 2;

        // Transfer base asset to recipient.
        transfer(Identity::Address(recipient), BASE_ASSET, amount_to_transfer);
    }
}\n```

Sway Applications

The Sway-Applications Repository contains end-to-end example applications that are written in Sway in order to demonstrate what can be built.

Asset Management

  • Airdrop is an asset distribution program where users are able to claim assets given a valid merkle proof.
  • Escrow is a third party that keeps an asset on behalf of multiple parties.
  • Non-Fungible Native Asset (NFT) is an asset contract which provides unique collectibles, identified and differentiated by IDs, where assets contain metadata giving them distinctive characteristics.
  • Fractional Non-Fungible Token (F-NFT) is a token contract which issues shares or partial ownership upon locking an NFT into a vault.
  • Timelock is a contract which restricts the execution of a transaction to a specified time range.
  • Native Asset is a basic asset contract that enables the use of Native Assets on Fuel using existing standards and libraries.

Decentralized Finance

  • English Auction is an auction where users bid up the price of an asset until the bidding period has ended or a reserve has been met.
  • Fundraiser is a program allowing users to pledge towards a goal.
  • OTC Swap Predicate is a predicate that can be used to propose and execute an atomic swap between two parties without requiring any on-chain state.

Governance

Games

  • TicTacToe is a game where two players compete to align three markers in a row.

Other

  • Counter-Script is a script that calls a contract to increment a counter.
  • Name-Registry allows users to perform transactions with human readable names instead of addresses.
  • Oracle is a smart contract that provides off-chain data to on-chain applications.

Sway Program Types

A Sway program itself has a type: it is either a contract, a predicate, a script, or a library. The first three of these things are all deployable to the blockchain. A library is simply a project designed for code reuse and is never directly deployed to the chain.

Every Sway file must begin with a declaration of what type of program it is. A project can have many libraries within it, but only one contract, script, or predicate. Scripts and predicates require main functions to serve as entry points, while contracts instead publish an ABI. This chapter will go into detail about all of these various types of programs and what purposes they serve.

Contracts are used primarily for protocols or systems that operate within a fixed set of rules. A good example would be a staking contract or a decentralized exchange (also called a DEX).

Scripts are used for complex on-chain interactions that won't persist. An example of this may be using a DEX and Lender to create a leveraged position (borrow, swap, re-collateralize) which is a complex transaction that would usually take multiple steps.

Libraries are for code that is reusable and useful for handling common situations. A good example of this would be a library to handle fixed-point math or big number math.

What is a Smart Contract?

A smart contract is no different than a script or predicate in that it is a piece of bytecode that is deployed to the blockchain via a transaction. The main features of a smart contract that differentiate it from scripts or predicates are that it is callable and stateful. Put another way, a smart contract is analogous to a deployed API with some database state.

The interface of a smart contract, also just called a contract, must be defined strictly with an ABI declaration. See this contract for an example.

Syntax of a Smart Contract

As with any Sway program, the program starts with a declaration of what program type it is. A contract must also either define or import an ABI declaration and implement it.

It is considered good practice to define your ABI in a separate library and import it into your contract. This allows callers of your contract to simply import the ABI directly and use it in their scripts to call your contract.

Let's take a look at an ABI declaration in a library:

```sway\n// ANCHOR: abi_library
library;

// ANCHOR: abi
abi Wallet {
    // ANCHOR: receive_funds
    #[storage(read, write), payable]
    fn receive_funds();
    // ANCHOR_END: receive_funds

    // ANCHOR: send_funds
    #[storage(read, write)]
    fn send_funds(amount_to_send: u64, recipient_address: Address);
    // ANCHOR_END: send_funds
}
// ANCHOR: abi
// ANCHOR_END: abi_library\n```

Let's focus on the ABI declaration and inspect it line-by-line.

The ABI Declaration

```sway\n// ANCHOR: abi_library
library;

// ANCHOR: abi
abi Wallet {
    // ANCHOR: receive_funds
    #[storage(read, write), payable]
    fn receive_funds();
    // ANCHOR_END: receive_funds

    // ANCHOR: send_funds
    #[storage(read, write)]
    fn send_funds(amount_to_send: u64, recipient_address: Address);
    // ANCHOR_END: send_funds
}
// ANCHOR: abi
// ANCHOR_END: abi_library\n```

In the first line, abi Wallet {, we declare the name of this Application Binary Interface, or ABI. We are naming this ABI Wallet. To import this ABI into either a script for calling or a contract for implementing, you would use

```sway\n// ANCHOR: full_wallet
contract;

use std::{asset::transfer, call_frames::msg_asset_id, context::msg_amount};

// ANCHOR: abi_import
use wallet_abi::Wallet;
// ANCHOR_END: abi_import
const OWNER_ADDRESS = Address::from(0x8900c5bec4ca97d4febf9ceb4754a60d782abbf3cd815836c1872116f203f861);

storage {
    balance: u64 = 0,
}

// ANCHOR: abi_impl
impl Wallet for Contract {
    #[storage(read, write), payable]
    fn receive_funds() {
        if msg_asset_id() == AssetId::base() {
            // If we received the base asset then keep track of the balance.
            // Otherwise, we're receiving other native assets and don't care
            // about our balance of coins.
            storage.balance.write(storage.balance.read() + msg_amount());
        }
    }

    #[storage(read, write)]
    fn send_funds(amount_to_send: u64, recipient_address: Address) {
        let sender = msg_sender().unwrap();
        match sender {
            Identity::Address(addr) => assert(addr == OWNER_ADDRESS),
            _ => revert(0),
        };

        let current_balance = storage.balance.read();
        assert(current_balance >= amount_to_send);

        storage.balance.write(current_balance - amount_to_send);

        // Note: `transfer()` is not a call and thus not an
        // interaction. Regardless, this code conforms to
        // checks-effects-interactions to avoid re-entrancy.
        transfer(
            Identity::Address(recipient_address),
            AssetId::base(),
            amount_to_send,
        );
    }
}
// ANCHOR_END: abi_impl
// ANCHOR_END: full_wallet\n```

In the second line,

```sway\n// ANCHOR: abi_library
library;

// ANCHOR: abi
abi Wallet {
    // ANCHOR: receive_funds
    #[storage(read, write), payable]
    fn receive_funds();
    // ANCHOR_END: receive_funds

    // ANCHOR: send_funds
    #[storage(read, write)]
    fn send_funds(amount_to_send: u64, recipient_address: Address);
    // ANCHOR_END: send_funds
}
// ANCHOR: abi
// ANCHOR_END: abi_library\n```

we are declaring an ABI method called receive_funds which, when called, should receive funds into this wallet. Note that we are simply defining an interface here, so there is no function body or implementation of the function. We only need to define the interface itself. In this way, ABI declarations are similar to trait declarations. This particular ABI method does not take any parameters.


In the third line,

```sway\n// ANCHOR: abi_library
library;

// ANCHOR: abi
abi Wallet {
    // ANCHOR: receive_funds
    #[storage(read, write), payable]
    fn receive_funds();
    // ANCHOR_END: receive_funds

    // ANCHOR: send_funds
    #[storage(read, write)]
    fn send_funds(amount_to_send: u64, recipient_address: Address);
    // ANCHOR_END: send_funds
}
// ANCHOR: abi
// ANCHOR_END: abi_library\n```

we are declaring another ABI method, this time called send_funds. It takes two parameters: the amount to send, and the address to send the funds to.

Note: The ABI methods receive_funds and send_funds also require the annotation #[storage(read, write)] because their implementations require reading and writing a storage variable that keeps track of the wallet balance, as we will see shortly. Refer to Purity for more information on storage annotations.

Implementing an ABI for a Smart Contract

Now that we've discussed how to define the interface, let's discuss how to use it. We will start by implementing the above ABI for a specific contract.

Implementing an ABI for a contract is accomplished with impl <ABI name> for Contract syntax. The for Contract syntax can only be used to implement an ABI for a contract; implementing methods for a struct should use impl Foo syntax.

```sway\n// ANCHOR: full_wallet
contract;

use std::{asset::transfer, call_frames::msg_asset_id, context::msg_amount};

// ANCHOR: abi_import
use wallet_abi::Wallet;
// ANCHOR_END: abi_import
const OWNER_ADDRESS = Address::from(0x8900c5bec4ca97d4febf9ceb4754a60d782abbf3cd815836c1872116f203f861);

storage {
    balance: u64 = 0,
}

// ANCHOR: abi_impl
impl Wallet for Contract {
    #[storage(read, write), payable]
    fn receive_funds() {
        if msg_asset_id() == AssetId::base() {
            // If we received the base asset then keep track of the balance.
            // Otherwise, we're receiving other native assets and don't care
            // about our balance of coins.
            storage.balance.write(storage.balance.read() + msg_amount());
        }
    }

    #[storage(read, write)]
    fn send_funds(amount_to_send: u64, recipient_address: Address) {
        let sender = msg_sender().unwrap();
        match sender {
            Identity::Address(addr) => assert(addr == OWNER_ADDRESS),
            _ => revert(0),
        };

        let current_balance = storage.balance.read();
        assert(current_balance >= amount_to_send);

        storage.balance.write(current_balance - amount_to_send);

        // Note: `transfer()` is not a call and thus not an
        // interaction. Regardless, this code conforms to
        // checks-effects-interactions to avoid re-entrancy.
        transfer(
            Identity::Address(recipient_address),
            AssetId::base(),
            amount_to_send,
        );
    }
}
// ANCHOR_END: abi_impl
// ANCHOR_END: full_wallet\n```

You may notice once again the similarities between traits and ABIs. And, indeed, as a bonus, you can define methods in addition to the interface surface of an ABI, just like a trait. These pre-implemented ABI methods automatically become available as part of the contract interface that implements the corresponding ABI.

Note that the above implementation of the ABI follows the Checks, Effects, Interactions pattern.

The ContractId type

Contracts have an associated ContractId type in Sway. The ContractId type allows for Sway programs to refer to contracts in the Sway language. Please refer to the ContractId section of the book for more information on ContractIds.

Calling a Smart Contract from a Script

Note: In most cases, calling a contract should be done from the Rust SDK or the TypeScript SDK which provide a more ergonomic UI for interacting with a contract. However, there are situations where manually writing a script to call a contract is required.

Now that we have defined our interface and implemented it for our contract, we need to know how to actually call our contract. Let's take a look at a contract call:

```sway\nscript;

use wallet_abi::Wallet;

fn main() {
    let contract_address = 0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b;
    let caller = abi(Wallet, contract_address);
    let amount_to_send = 200;
    let recipient_address = Address::from(0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b);
    caller
        .send_funds {
            gas: 10000,
            coins: 0,
            asset_id: b256::zero(),
        }(amount_to_send, recipient_address);
}\n```

The main new concept is the abi cast: abi(AbiName, contract_address). This returns a ContractCaller type which can be used to call contracts. The methods of the ABI become the methods available on this contract caller: send_funds and receive_funds. We then directly call the contract ABI method as if it was just a regular method. You also have the option of specifying the following special parameters inside curly braces right before the main list of parameters:

  1. gas: a u64 that represents the gas being forwarded to the contract when it is called.
  2. coins: a u64 that represents how many coins are being forwarded with this call.
  3. asset_id: a b256 that represents the ID of the asset type of the coins being forwarded.

Each special parameter is optional and assumes a default value when skipped:

  1. The default value for gas is the context gas (i.e. the content of the special register $cgas). Refer to the FuelVM specifications for more information about context gas.
  2. The default value for coins is 0.
  3. The default value for asset_id is b256::zero().

Libraries

Libraries in Sway are files used to define new common behavior.

The most prominent example of this is the Sway Standard Library that is made implicitly available to all Forc projects created using forc new.

Writing Libraries

Libraries are defined using the library keyword at the beginning of a file, followed by a name so that they can be imported.

library;

// library code

A good reference library to use when learning library design is the Sway Standard Library. For example, the standard library offers an implementation of enum Option<T> which is a generic type that represents either the existence of a value using the variant Some(..) or a value's absence using the variant None. The Sway file implementing Option<T> has the following structure:

  • The library keyword:
library;
  • A use statement that imports revert from another library inside the standard library:
use ::revert::revert;
  • The enum definition which starts with the keyword pub to indicate that this Option<T> is publicly available outside the option library:
pub enum Option<T> {
    // variants
}
  • An impl block that implements some methods for Option<T>:
impl<T> Option<T> {

    fn is_some(self) -> bool {
        // body of is_some
    }

    // other methods
}

Now that the library option is fully written, and because Option<T> is defined with the pub keyword, we are now able to import Option<T> using use std::option::Option; from any Sway project and have access to all of its variants and methods. That being said, Option is automatically available in the standard library prelude so you never actually have to import it manually.

Libraries are composed of just a Forc.toml file and a src directory, unlike contracts which usually contain a tests directory and a Cargo.toml file as well. An example of a library's Forc.toml:

[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "lib.sw"
license = "Apache-2.0"
name = "my_library"

[dependencies]

which denotes the authors, an entry file, the name by which it can be imported, and any dependencies.

For large libraries, it is recommended to have a lib.sw entry point re-export all other sub-libraries.

The mod keyword registers a submodule, making its items (such as functions and structs) accessible from the parent library. If used at the top level it will refer to a file in the src folder and in other cases in a folder named after the library in which it is defined.

For example, the lib.sw of the standard library looks like:

library;

mod block;
mod storage;
mod constants;
mod vm;
// .. Other deps

with other libraries contained in the src folder, like the vm library (inside of src/vm.sw):

library;

mod evm;
// ...

and it's own sub-library evm located in src/vm/evm.sw:

library;

// ...

Using Libraries

There are two types of Sway libraries, based on their location and how they can be imported.

Internal Libraries

Internal libraries are located within the project's src directory alongside main.sw or in the appropriate folders as shown below:

$ tree
.
├── Cargo.toml
├── Forc.toml
└── src
    ├── internal_lib.sw
    ├── main.sw
    └── internal_lib
        └── nested_lib.sw

As internal_lib is an internal library, it can be imported into main.sw as follows:

  • Use the mod keyword followed by the library name to make the internal library a dependency
  • Use the use keyword with a :: separating the name of the library and the imported item(s)
mod internal_lib; // Assuming the library name in `internal_lib.sw` is `internal_lib`

use internal_lib::mint;

// `mint` from `internal_library` is now available in this file

External Libraries

External libraries are located outside the main src directory as shown below:

$ tree
.
├── my_project
│   ├── Cargo.toml
│   ├── Forc.toml
│   └─── src
│       └── main.sw
│
└── external_lib
    ├── Cargo.toml
    ├── Forc.toml
    └─── src
        └── lib.sw

As external_lib is outside the src directory of my_project, it needs to be added as a dependency in the Forc.toml file of my_project, by adding the library path in the dependencies section as shown below, before it can be imported:

[dependencies]
external_library = { path = "../external_library" }

Once the library dependency is added to the toml file, you can import items from it as follows:

  • Make sure the item you want imported are declared with the pub keyword (if applicable, for instance: pub fn mint() {})
  • Use the use keyword to selectively import items from the library
use external_library::mint;

// `mint` from `external_library` is now available in this file

Wildcard imports using * are supported, but it is generally recommended to use explicit imports where possible.

Note: the standard library is implicitly available to all Forc projects, that is, you are not required to manually specify std as an explicit dependency in Forc.toml.

Reference Sway Libraries

The repository sway-libs is a collection of external libraries that you can import and make use of in your Fuel applications. These libraries are meant to be implementations of common use-cases valuable for dapp development.

Some Sway Libraries to try out:

Example

You can import and use a Sway Library such as the Ownership library just like any other external library.

use ownership::Ownership;

Once imported, you can use the following basic functionality of the library in your smart contract:

  • Declaring an owner
  • Changing ownership
  • Renouncing ownership
  • Ensuring a function may only be called by the owner

Scripts

A script is runnable bytecode on the chain which executes once to perform some task. It does not represent ownership of any resources and it cannot be called by a contract. A script can return a single value of any type.

Scripts are state-aware in that while they have no persistent storage (because they only exist during the transaction) they can call contracts and act based upon the returned values and results.

This example script calls a contract:

```sway\nscript;

use wallet_abi::Wallet;

fn main() {
    let contract_address = 0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b;
    let caller = abi(Wallet, contract_address);
    let amount_to_send = 200;
    let recipient_address = Address::from(0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b);
    caller
        .send_funds {
            gas: 10000,
            coins: 0,
            asset_id: b256::zero(),
        }(amount_to_send, recipient_address);
}\n```

Scripts, similar to predicates, rely on a main() function as an entry point. You can call other functions defined in a script from the main() function or call another contract via an ABI cast.

An example use case for a script would be a router that trades funds through multiple decentralized exchanges to get the price for the input asset, or a script to re-adjust a Collateralized Debt Position via a flash loan.

Scripts and the SDKs

Unlike EVM transactions which can call a contract directly (but can only call a single contract), Fuel transactions execute a script, which may call zero or more contracts. The Rust and TypeScript SDKs provide functions to call contract methods as if they were calling contracts directly. Under the hood, the SDKs wrap all contract calls with scripts that contain minimal code to simply make the call and forward script data as call parameters.

Predicates

From the perspective of Sway, predicates are programs that return a Boolean value and which represent ownership of some resource upon execution to true. They have no access to contract storage. Here is a trivial predicate, which always evaluates to true:

predicate;

// All predicates require a main function which returns a Boolean value.
fn main() -> bool {
    true
}

The address of this predicate is 0xd19a5fe4cb9baf41ad9813f1a6fef551107c8e8e3f499a6e32bccbb954a74764. Any assets sent to this address can be unlocked or claimed by executing the predicate above as it always evaluates to true.

It does not need to be deployed to a blockchain because it only exists during a transaction. That being said, the predicate address is on-chain as the owner of one or more UTXOs.

Transfer Coins to a Predicate

In Fuel, coins can be sent to a predicate's address(the bytecode root, calculated here).

Spending Predicate Coins

The coin UTXOs become spendable not on the provision of a valid signature, but rather if the supplied predicate both has a root that matches their owner, and evaluates to true.

If a predicate reverts, or tries to access impure VM opcodes, the evaluation is automatically false.

An analogy for predicates is rather than a traditional 12 or 24 word seed phrase that generates a private key and creates a valid signature, a predicate's code can be viewed as the private key. Anyone with the code may execute a predicate, but only when the predicate evaluates to true may the assets owned by that address be released.

Spending Conditions

Predicates may introspect the transaction spending their coins (inputs, outputs, script bytecode, etc.) and may take runtime arguments, either or both of which may affect the evaluation of the predicate.

It is important to note that predicates cannot read or write memory. They may however check the inputs and outputs of a transaction. For example in the OTC Predicate Swap Example, a user may specify they would like to swap asset1 for asset2 and with amount of 5. The user would then send asset1 to the predicate. Only when the predicate can verify that the outputs include 5 coins of asset2 being sent to the original user, may asset1 be transferred out of the predicate.

Debugging Predicates

Because they don't have any side effects (they are pure), predicates cannot create receipts. Therefore, they cannot have logging or create a stack backtrace. This means that there is no native way to debug them aside from using a single-stepping debugger.

As a workaround, the predicate can be written, tested, and debugged first as a script, and then changed back into a predicate.

Sway Language basics

Sway is a programming language designed for the FuelVM. It is a statically typed, compiled language with type inference and traits. Sway aims to make smart contract development safer and more efficient through the use of strong static analysis and compiler feedback.

Get started with the basics of Sway:

Variables

Variables in Sway are immutable by default. This means that, by default, once a variable is declared, its value cannot change. This is one of the ways how Sway encourages safe programming, and many modern languages have this same default.

Let's take a look at variables in detail.

Declaring a Variable

Let's look at a variable declaration:

let foo = 5;

Great! We have just declared a variable, foo. What do we know about foo?

  1. It is immutable.
  2. Its value is 5.
  3. Its type is u64, a 64-bit unsigned integer.

u64 is the default numeric type, and represents a 64-bit unsigned integer. See the section Built-in Types for more details.

We can also make a mutable variable. Let's take a look:

let mut foo = 5;
foo = 6;

Now, foo is mutable, and the reassignment to the number 6 is valid. That is, we are allowed to mutate the variable foo to change its value.

When assigning to a mutable variable, the right-hand side of the assignment is evaluated before the left-hand side. In the below example, the mutable variable i will first be increased and the resulting value of 1 will be stored to array[1], thus resulting in array being changed to [0, 1, 0].

let mut array = [0, 0, 0];
let mut i = 0;

array[i] = {
    i += 1;
    i
};

Type Annotations

A variable declaration can contain a type annotation. A type annotation serves the purpose of declaring the type, in addition to the value, of a variable.

Let's take a look:

let foo: u32 = 5;

We have just declared the type of the variable foo as a u32, which is an unsigned 32-bit integer. Let's take a look at a few other type annotations:

let bar: str[4] = __to_str_array("sway");
let baz: bool = true;

If the value declared cannot be assigned to the declared type, there will be an error generated by the compiler.

Built-in Types

Every value in Sway is of a certain type. Although deep down, all values are just ones and zeroes in the underlying virtual machine, Sway needs to know what those ones and zeroes actually mean. This is accomplished with types.

Sway is a statically typed language. At compile time, the types of every value must be known. This does not mean you need to specify every single type: usually, the type can be reasonably inferred by the compiler.

Primitive Types

Sway has the following primitive types:

  1. () (unit type)
  2. u8 (8-bit unsigned integer)
  3. u16 (16-bit unsigned integer)
  4. u32 (32-bit unsigned integer)
  5. u64 (64-bit unsigned integer)
  6. u256 (256-bit unsigned integer)
  7. str[] (fixed-length string)
  8. str (string slices)
  9. bool (Boolean true or false)
  10. b256 (256 bits (32 bytes), i.e. a hash)

All other types in Sway are built up of these primitive types, or references to these primitive types. You may notice that there are no signed integers—this is by design. In the blockchain domain that Sway occupies, floating-point values and negative numbers have smaller utility, so their implementation has been left up to libraries for specific use cases.

Unit Type

The unit type, (), is a type that allows only one value, and thus, represents a value with no information. It is used to indicate the absence of a meaningful value, or the result of a function that performs an action, but does not return any data. The value of the unit type, called simply unit, has the same symbol as the unit type, (). Unit type in Sway serves a similar purpose as void in imperative languages like C or Java.

For example:

fn returns_unit() -> () { // Here, `()` represent the unit type.
    ()                    // Here, `()` represents the single unit value of the unit type.
}

In Sway, if the function return type is not specified, it is () by default. Thus, the above example is semantically same as the following:

fn returns_unit() {
}

Numeric Types

All of the unsigned integer types are numeric types.

Numbers can be declared with binary syntax, hexadecimal syntax, base-10 syntax, and underscores for delineation. Let's take a look at the following valid numeric primitives:

0xffffff    // hexadecimal
0b10101010  // binary
10          // base-10
100_000     // underscore delineated base-10
0x1111_0000 // underscore delineated binary
0xfff_aaa   // underscore delineated hexadecimal

The default numeric type is u64. The FuelVM's word size is 64 bits, and the cases where using a smaller numeric type saves space are minimal.

If a 64-bit or 256-bit arithmetic operation produces an overflow or an underflow, computation gets reverted automatically by FuelVM.

8/16/32-bit arithmetic operations are emulated using their 64-bit analogues with additional overflow/underflow checks inserted, which generally results in somewhat higher gas consumption.

The same does not happen with 256-bit operations, including b256, which uses specialized operations and are as efficient as possible.

Boolean Type

The boolean type (bool) has two potential values: true or false. Boolean values are typically used for conditional logic or validation, for example in if expressions. Booleans can be negated, or flipped, with the unary negation operator !.

For example:

fn returns_false() -> bool {
    let boolean_value: bool = true;
    !boolean_value
}

String Slices

In Sway, string literals are stored as variable length string slices. Which means that they are stored as a pointer to the actual string data and its length.

let my_string: str = "fuel";

String slices, because they contain pointers have limited usage. They cannot be used as constants, storage fields, or configurable constants, nor as main function arguments or returns.

For these cases one must use string arrays, as described below.

String Arrays

In Sway, static-length strings are a primitive type. This means that when you declare a string array, its size is a part of its type. This is necessary for the compiler to know how much memory to give for the storage of that data. The size of the string is denoted with square brackets.

Let's take a look:

let my_string: str[4] = __to_str_array("fuel");

Because the string literal "fuel" is four letters, the type is str[4], denoting a static length of 4 characters. Strings default to UTF-8 in Sway.

As above, string literals are typed as string slices. So that is why the need for __to_str_array that convert them to string arrays at compile time.

Conversion during runtime can be done with from_str_array and try_as_str_array. The latter can fail, given that the specified string array must be big enough for the string slice content.

let a: str = "abcd";
let b: str[4] = a.try_as_str_array().unwrap();
let c: str = from_str_array(b);

Compound Types

Compound types are types that group multiple values into one type. In Sway, we have arrays and tuples.

Tuple Types

A tuple is a general-purpose static-length aggregation of types. In more plain terms, a tuple is a single type that consists of an aggregate of zero or more types. The internal types that make up a tuple, and the tuple's arity, define the tuple's type.

Let's take a look at some examples.

let x: (u64, u64) = (0, 0);

This is a tuple, denoted by parenthesized, comma-separated values. Note that the type annotation, (u64, u64), is similar in syntax to the expression which instantiates that type, (0, 0).

let x: (u64, bool) = (42, true);
assert(x.1);

In this example, we have created a new tuple type, (u64, bool), which is a composite of a u64 and a bool.

To access a value within a tuple, we use tuple indexing: x.1 stands for the first (zero-indexed, so the bool) value of the tuple. Likewise, x.0 would be the zeroth, u64 value of the tuple. Tuple values can also be accessed via destructuring.

struct Foo {}
let x: (u64, Foo, bool) = (42, Foo {}, true);
let (number, foo, boolean) = x;

To create one-arity tuples, we will need to add a trailing comma:

let x: u64 = (42);     // x is of type u64
let y: (u64) = (42);   // y is of type u64
let z: (u64,) = (42,); // z is of type (u64), i.e. a one-arity tuple
let w: (u64) = (42,);  // type error

Arrays

An array is similar to a tuple, but an array's values must all be of the same type. Arrays can hold arbitrary types including non-primitive types.

An array is written as a comma-separated list inside square brackets:

let x = [1, 2, 3, 4, 5];

Arrays are allocated on the stack since their size is known. An array's size is always static, i.e. it cannot change. An array of five elements cannot become an array of six elements.

Arrays can be iterated over, unlike tuples. An array's type is written as the type the array contains followed by the number of elements, semicolon-separated and within square brackets, e.g., [u64; 5]. To access an element in an array, use the array indexing syntax, i.e. square brackets.

Array elements can also be mutated if the underlying array is declared as mutable:

let mut x = [1, 2, 3, 4, 5];
x[0] = 0;
```sway\nscript;

struct Foo {
    f1: u32,
    f2: b256,
}

fn main() {
    // Array of integers with type ascription
    let array_of_integers: [u8; 5] = [1, 2, 3, 4, 5];

    // Array of strings
    let array_of_strings = ["Bob", "Jan", "Ron"];

    // Array of structs
    let array_of_structs: [Foo; 2] = [
        Foo {
            f1: 11,
            f2: 0x1111111111111111111111111111111111111111111111111111111111111111,
        },
        Foo {
            f1: 22,
            f2: 0x2222222222222222222222222222222222222222222222222222222222222222,
        },
    ];

    // Accessing an element of an array
    let mut array_of_bools: [bool; 2] = [true, false];
    assert(array_of_bools[0]);

    // Mutating the element of an array
    array_of_bools[1] = true;
    assert(array_of_bools[1]);
}\n```

Commonly Used Library Types

The Sway Standard Library is the foundation of portable Sway software, a set of minimal shared abstractions for the broader Sway ecosystem. It offers core types, library-defined operations on language primitives, native asset management, blockchain contextual operations, access control, storage management, and support for types from other VMs, among many other things. Reference the standard library docs here.

Result<T, E>

Type Result is the type used for returning and propagating errors. It is an enum with two variants: Ok(T), representing success and containing a value, and Err(E), representing error and containing an error value. The T and E in this definition are type parameters, allowing Result to be generic and to be used with any types.

```sway\n//! Error handling with the `Result` type.
//!
//! `Result<T, E>` `Result` is the type used for returning and propagating
//! errors. It is an enum with the variants, `Ok(T)`, representing
//! success and containing a value, and `Err(E)`, representing error
//! and containing an error value.
//!
//! Functions return `Result` whenever errors are expected and recoverable. In
//! the `std` crate, `Result` is most prominently used for `Identity`
//! interactions and cryptographic operations.
//!
//! A simple function returning `Result` might be defined and used like so:
//!
//! ```
//! enum Version {
//!     Version1,
//!     Version2,
//! }
//!
//! enum VersionError {
//!     InvalidNumber,
//! }
//!
//! fn parse_version(version_number: u8) -> Result<Version, VersionError> {
//!     match version_number {
//!         1 => Ok(Version::Version1),
//!         2 => Ok(Version::Version2),
//!         _ => Err(VersionError::InvalidNumber),
//!     }
//! }
//! ```
//!
//! ### Method overview
//!
//! In addition to working with pattern matching, `Result` provides a variety
//! of methods.
//!
//! ### Querying the variant
//!
//! The `is_ok` and `is_err` methods return `true` if the `Result` is
//! `Ok` or `Err`, respectively.
//!
//! `is_ok` : `Result::is_ok`
//! `is_err`: `Result::is_err`
//!
//! ### Extracting the contained value
//!
//! These methods extract the contained value in a `Result<T,E>` when it is
//! the `Ok` variant. If the `Result` is `Err`:
//!
//! * `unwrap` reverts.
//! * `unwrap_or` returns the default provided value.
//!
//! `unwrap`   : `Result::unwrap`
//! `unwrap_or`: `Result::unwrap_or`
library;

use ::logging::log;
use ::revert::revert;
use ::codec::*;
use ::ops::*;

// ANCHOR: docs_result
/// `Result` is a type that represents either success (`Ok`) or failure (`Err`).
pub enum Result<T, E> {
    /// Contains the success value.
    Ok: T,
    /// Contains the error value.
    Err: E,
}
// ANCHOR_END: docs_result


// Type implementation
//
impl<T, E> Result<T, E> {
    // Querying the contained values
    //
    /// Returns whether a result contains a success value.
    ///
    /// # Returns
    ///
    /// * [bool] - Returns `true` if the result is `Ok`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// enum Error {
    ///     NotFound,
    ///     Invalid,
    /// }
    ///
    /// fn foo() {
    ///     let x: Result<u64, Error> = Result::Ok(42);
    ///     assert(x.is_ok());
    ///
    ///     let y: Result<u64, Error> = Result::Err(Error::NotFound));
    ///     assert(!y.is_ok());
    /// }
    /// ```
    pub fn is_ok(self) -> bool {
        match self {
            Self::Ok(_) => true,
            _ => false,
        }
    }

    /// Returns whether a result contains an error value.
    ///
    /// # Returns
    ///
    /// * [bool] - Returns `true` if the result is `Err`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// enum Error {
    ///     NotFound,
    ///     Invalid,
    /// }
    ///
    /// fn foo() {
    ///     let x: Result<u64, Error> = Result::Ok(42);
    ///     assert(!x.is_err());
    ///
    ///     let y: Result<u64, Error> = Result::Err(Error::NotFound));
    ///     assert(y.is_err());
    /// }
    /// ```
    pub fn is_err(self) -> bool {
        match self {
            Self::Ok(_) => false,
            _ => true,
        }
    }

    /// Returns the contained `Ok` value, consuming the `self` value.
    ///
    /// # Additional Information
    ///
    /// Because this function may revert, its use is generally discouraged.
    /// Instead, prefer to use pattern matching and handle the `Err`
    /// case explicitly.
    ///
    /// # Returns
    ///
    /// * [T] - The value contained by the result.
    ///
    /// # Reverts
    ///
    /// * Reverts if the `Result` is the `Err` variant.
    ///
    /// # Examples
    ///
    /// ```sway
    /// enum Error {
    ///     NotFound,
    ///     Invalid,
    /// }
    ///
    /// fn foo() {
    ///     let x: Result<u64, Error> = Result::Ok(42);
    ///     assert(x.unwrap() == 42);
    ///
    ///     let y: Result<u64, Error> = Result::Err(Error::NotFound));
    ///     let val = y.unwrap(); // reverts
    /// }
    /// ```
    pub fn unwrap(self) -> T {
        match self {
            Self::Ok(inner_value) => inner_value,
            _ => revert(0),
        }
    }

    /// Returns the contained `Ok` value or a provided default.
    ///
    /// # Arguments
    ///
    /// * `default`: [T] - The value that is the default.
    ///
    /// # Returns
    ///
    /// * [T] - The value of the result or the default.
    ///
    /// # Examples
    ///
    /// ```sway
    /// enum Error {
    ///     NotFound,
    ///     Invalid,
    /// }
    ///
    /// fn foo() {
    ///     let x: Result<u64, Error> = Result::Ok(42);
    ///     assert(x.unwrap_or(69) == 42);
    ///
    ///     let y: Result<u64, Error> = Result::Err(Error::NotFound));
    ///     assert(y.unwrap_or(69) == 69);
    /// }
    /// ```
    pub fn unwrap_or(self, default: T) -> T {
        match self {
            Self::Ok(inner_value) => inner_value,
            Self::Err(_) => default,
        }
    }

    /// Returns the contained `Ok` value, consuming the `self` value.
    /// If the `Result` is the `Err` variant, logs the provided message, along with the error value.
    ///
    /// # Additional Information
    ///
    /// Because this function may revert, its use is generally discouraged.
    /// Instead, prefer to use pattern matching and handle the `Err`
    /// case explicitly.
    ///
    /// # Arguments
    ///
    /// * `msg`: [M] - The message to be logged if the `Result` is the `Err` variant.
    ///
    /// # Returns
    ///
    /// * [T] - The value contained by the result.
    ///
    /// # Reverts
    ///
    /// * Reverts if the `Result` is the `Err` variant.
    ///
    /// # Examples
    ///
    /// ```sway
    /// enum Error {
    ///     NotFound,
    ///     Invalid,
    /// }
    ///
    /// fn foo() {
    ///     let x: Result<u64, Error> = Result::Ok(42);
    ///     assert(x.expect("X is known to be 42") == 42);
    ///
    ///     let y: Result<u64, Error> = Result::Err(Error::NotFound));
    ///     let val = y.expect("Testing expect"); // reverts with `("Testing Expect", "Error::NotFound")`
    /// }
    /// ```
    ///
    /// # Recommended Message Style
    ///
    /// We recommend that `expect` messages are used to describe the reason you *expect* the `Result` should be `Ok`.
    ///
    /// ```sway
    /// let x: Result<u64, Error> = bar(1);
    /// let value = x.expect("bar() should never return Err with 1 as an argument");
    /// ```
    pub fn expect<M>(self, msg: M) -> T
    where
        M: AbiEncode,
        E: AbiEncode,
    {
        match self {
            Self::Ok(v) => v,
            Self::Err(err) => {
                log((msg, err));
                revert(0);
            },
        }
    }

    // TODO: Implement the following transforms when Option and Result can
    // import one another:
    // - `ok(self) -> Option<T>`
    // - `err(self) -> Option<E>`
}

impl<T, E> PartialEq for Result<T, E>
where
    T: PartialEq,
    E: PartialEq,
{
    fn eq(self, other: Self) -> bool {
        match (self, other) {
            (Self::Ok(a), Self::Ok(b)) => a == b,
            (Self::Err(a), Self::Err(b)) => a == b,
            _ => false,
        }
    }
}
impl<T, E> Eq for Result<T, E>
where
    T: Eq,
    E: Eq,
{}\n```

Functions return Result whenever errors are expected and recoverable.

Take the following example:

```sway\nscript;

enum MyContractError {
    DivisionByZero: (),
}

fn divide(numerator: u64, denominator: u64) -> Result<u64, MyContractError> {
    if (denominator == 0) {
        return Err(MyContractError::DivisionByZero);
    } else {
        Ok(numerator / denominator)
    }
}

fn main() -> Result<u64, str[4]> {
    let result = divide(20, 2);
    match result {
        Ok(value) => Ok(value),
        Err(MyContractError::DivisionByZero) => Err(__to_str_array("Fail")),
    }
}\n```

Option<T>

Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not. Option types are very common in Sway code, as they have a number of uses:

  • Initial values where None can be used as an initializer.
  • Return value for otherwise reporting simple errors, where None is returned on error.

The implementation of Option matches on the variant: if it's Ok it returns the inner value, if it's None, it reverts.

```sway\n//! A type for optional values.
//!
//! Type `Option` represents an optional value: every `Option`
//! is either `Some` and contains a value, or `None`, and
//! does not. `Option` types are very common in Sway code, as
//! they have a number of uses:
//!
//! * Initial values where `None` can be used as an initializer.
//! * Return value for otherwise reporting simple errors, where `None` is
//!   returned on error.
//! * Optional struct fields.
//! * Optional function arguments.
//!
//! `Option`s are commonly paired with pattern matching to query the presence
//! of a value and take action, always accounting for the `None` case.
//!
//! ```
//! fn divide(numerator: u64, denominator: u64) -> Option<u64> {
//!     if denominator == 0 {
//!         None
//!     } else {
//!         Some(numerator / denominator)
//!     }
//! }
//!
//! fn call_divide() {
//!     // The return value of the function is an option
//!     let result = divide(6, 2);
//!
//!     // Pattern match to retrieve the value
//!     match result {
//!         // The division was valid
//!         Some(x) => std::logging::log(x),
//!         // The division was invalid
//!         None    => std::logging::log("Cannot divide by 0"),
//!     }
//! }
//! ```
//!
//! # Method overview
//!
//! In addition to working with pattern matching, `Option` provides a wide
//! variety of different methods.
//!
//! # Querying the variant
//!
//! The `is_some` and `is_none` methods return `true` if the `Option`
//! is `Some` or `None`, respectively.
//!
//! `is_none`: `Option::is_none`
//! `is_some`: `Option::is_some`
//!
//! # Extracting the contained value
//!
//! These methods extract the contained value in an `Option<T>` when it
//! is the `Some` variant. If the `Option` is `None`:
//!
//! * `unwrap` reverts.
//! * `unwrap_or` returns the provided default value.
//!
//! `unwrap`   : `Option::unwrap`
//! `unwrap_or`: `Option::unwrap_or`
//!
//! # Transforming contained values
//!
//! These methods transform `Option` to `Result`:
//!
//! * `ok_or` transforms `Some(v)` to `Ok(v)`, and `None` to
//!   `Err(e)` using the provided default error value.
//!
//! `Err(e)` : `Result::Err`
//! `Ok(v)`  : `Result::Ok`
//! `Some(v)`: `Option::Some`
//! `ok_or`  : `Option::ok_or`
library;

use ::logging::log;
use ::result::Result;
use ::revert::revert;
use ::codec::*;
use ::ops::*;

// ANCHOR: docs_option
/// A type that represents an optional value, either `Some(val)` or `None`.
pub enum Option<T> {
    /// No value.
    None: (),
    /// Some value of type `T`.
    Some: T,
}
// ANCHOR_END: docs_option

impl<T> PartialEq for Option<T>
where
    T: PartialEq,
{
    fn eq(self, other: Self) -> bool {
        match (self, other) {
            (Option::Some(a), Option::Some(b)) => a == b,
            (Option::None, Option::None) => true,
            _ => false,
        }
    }
}
impl<T> Eq for Option<T>
where
    T: Eq,
{}

// Type implementation
//
impl<T> Option<T> {
    // Querying the contained values
    //
    /// Returns whether the option is the `Some` variant.
    ///
    /// # Returns
    ///
    /// * [bool] - Returns `true` if the option is `Some`, otherwise `false`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() {
    ///     let x: Option<u32> = Some(2);
    ///     assert(x.is_some());
    ///
    ///     let x: Option<u32> = None;
    ///     assert(!x.is_some());
    /// }
    /// ```
    pub fn is_some(self) -> bool {
        match self {
            Self::Some(_) => true,
            _ => false,
        }
    }

    /// Returns whether the option is the `None` variant.
    ///
    /// # Returns
    ///
    /// * [bool] - Returns `true` if the option is `None`, otherwise `false`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() {
    ///     let x: Option<u32> = Some(2);
    ///     assert(!x.is_none());
    ///
    ///     let x: Option<u32> = None;
    ///     assert(x.is_none());
    /// }
    /// ```
    pub fn is_none(self) -> bool {
        match self {
            Self::Some(_) => false,
            _ => true,
        }
    }

    // Getting to contained values
    //
    /// Returns the contained `Some` value, consuming the `self` value.
    ///
    /// # Additional Information
    ///
    /// Because this function may revert, its use is generally discouraged.
    /// Instead, use pattern matching and handle the `None`
    /// case explicitly, or call `unwrap_or`.
    ///
    /// # Returns
    ///
    /// * [T] - The value contained by the option.
    ///
    /// # Reverts
    ///
    /// * Reverts if the `Option` is the `None` variant.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() {
    ///     let x = Some(42);
    ///     assert(x.unwrap() == 42);
    /// }
    /// ```
    ///
    /// ```sway
    /// fn foo() {
    ///     let x: Option<u64> = None;
    ///     let value = x.unwrap(); // reverts
    /// }
    /// ```
    pub fn unwrap(self) -> T {
        match self {
            Self::Some(inner_value) => inner_value,
            _ => revert(0),
        }
    }

    /// Returns the contained `Some` value or a provided default.
    ///
    /// # Arguments
    ///
    /// * `default`: [T] - The default value the function will revert to.
    ///
    /// # Returns
    ///
    /// * [T] - The contained value or the default value.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() {
    ///     assert(Some(42).unwrap_or(69) == 42);
    ///     assert(None::<u64>().unwrap_or(69) == 69);
    /// }
    /// ```
    pub fn unwrap_or(self, default: T) -> T {
        match self {
            Self::Some(x) => x,
            Self::None => default,
        }
    }

    // Transforming contained values
    //
    /// Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to
    /// `Ok(v)` and `None` to `Err(e)`.
    ///
    /// # Additional Information
    ///
    /// `Ok(v)`  : `Result::Ok`
    /// `Err(e)` : `Result::Err`
    /// `Some(v)`: `Option::Some`
    /// `ok_or`  : `Option::ok_or`
    ///
    /// # Arguments
    ///
    /// * `err`: [E] - The error value if the option is `None`.
    ///
    /// # Returns
    ///
    /// * [Result<T, E>] - The result containing the value or the error.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() {
    ///     let x = Some(42);
    ///     match x.ok_or(0) {
    ///         Result::Ok(inner) => assert(inner == 42),
    ///         Result::Err => revert(0),
    ///     }
    ///
    ///     let x: Option<u64> = None;
    ///     match x.ok_or(0) {
    ///         Result::Ok(_) => revert(0),
    ///         Result::Err(e) => assert(e == 0),
    ///     }
    /// }
    /// ```
    pub fn ok_or<E>(self, err: E) -> Result<T, E> {
        match self {
            Self::Some(v) => Result::Ok(v),
            Self::None => Result::Err(err),
        }
    }

    /// Returns the contained `Some` value, consuming the `self` value.
    /// If the `Option` is the `None` variant, logs the provided message.
    ///
    /// # Additional Information
    ///
    /// Because this function may revert, its use is generally discouraged.
    /// Instead, prefer to use pattern matching and handle the `None`
    /// case explicitly.
    ///
    /// # Arguments
    ///
    /// * `msg`: [M] - The message to be logged if the `Option` is the `None` variant.
    ///
    /// # Returns
    ///
    /// * [T] - The value contained by the option.
    ///
    /// # Reverts
    ///
    /// * Reverts if the `Option` is the `None` variant.
    ///
    /// # Examples
    ///
    /// ```sway
    ///
    /// fn foo() {
    ///     let x: Option<u64> = Some(42);
    ///     assert(x.expect("X is known to be 42") == 42);
    ///
    ///     let y: Option<u64> = None;
    ///     let val = y.expect("Testing expect"); // reverts with `("Testing Expect")`
    /// }
    /// ```
    ///
    /// # Recommended Message Style
    ///
    /// We recommend that `expect` messages are used to describe the reason you *expect* the `Option` should be `Some`.
    ///
    /// ```sway
    /// let x: Option<u64> = bar(1);
    /// let value = x.expect("bar() should never return None with 1 as an argument");
    /// ```
    pub fn expect<M>(self, msg: M) -> T
    where
        M: AbiEncode,
    {
        match self {
            Self::Some(v) => v,
            Self::None => {
                log(msg);
                revert(0);
            },
        }
    }
}\n```

Option is commonly paired with pattern matching to query the presence of a value and take action, allowing developers to choose how to handle the None case.

Below is an example that uses pattern matching to handle invalid divisions by 0 by returning an Option:

```sway\nscript;

fn divide(numerator: u64, denominator: u64) -> Option<u64> {
    if denominator == 0 {
        None
    } else {
        Some(numerator / denominator)
    }
}

fn main() {
    let result = divide(6, 2);
    // Pattern match to retrieve the value
    match result {
        // The division was valid
        Some(x) => std::logging::log(x),
        // The division was invalid
        None => std::logging::log("Cannot divide by 0"),
    }
}\n```

Blockchain Types

Sway is fundamentally a blockchain language, and it offers a selection of types tailored for the blockchain use case.

These are provided via the standard library (lib-std) which both add a degree of type-safety, as well as make the intention of the developer more clear.

Address Type

The Address type is a type-safe wrapper around the primitive b256 type. Unlike the EVM, an address never refers to a deployed smart contract (see the ContractId type below). An Address can be either the hash of a public key (effectively an externally owned account if you're coming from the EVM) or the hash of a predicate. Addresses own UTXOs.

An Address is implemented as follows.

pub struct Address {
    value: b256,
}

Casting between the b256 and Address types must be done explicitly:

let my_number: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
let my_address: Address = Address::from(my_number);
let forty_two: b256 = my_address.into();

ContractId Type

The ContractId type is a type-safe wrapper around the primitive b256 type. A contract's ID is a unique, deterministic identifier analogous to a contract's address in the EVM. Contracts cannot own UTXOs but can own assets.

A ContractId is implemented as follows.

pub struct ContractId {
    value: b256,
}

Casting between the b256 and ContractId types must be done explicitly:

let my_number: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
let my_contract_id: ContractId = ContractId::from(my_number);
let forty_two: b256 = my_contract_id.into();

Getting a Contract's ContractId

To get the ContractId of a contract in an internal context use the ContractId::this() function:

impl MyContract for Contract {
    fn foo() {
        let this_contract_id: ContractId = ContractId::this();
    }
}

Identity Type

The Identity type is an enum that allows for the handling of both Address and ContractId types. This is useful in cases where either type is accepted, e.g., receiving funds from an identified sender, but not caring if the sender is an address or a contract.

An Identity is implemented as follows.

```sway\n//! A wrapper type with two variants, `Address` and `ContractId`.
//! The use of this type allows for handling interactions with contracts and addresses in a unified manner.
library;

use ::codec::*;
use ::assert::assert;
use ::address::Address;
use ::alias::SubId;
use ::asset_id::AssetId;
use ::contract_id::ContractId;
use ::hash::{Hash, Hasher};
use ::option::Option::{self, *};
use ::ops::*;

/// The `Identity` type: either an `Address` or a `ContractId`.
// ANCHOR: docs_identity
pub enum Identity {
    Address: Address,
    ContractId: ContractId,
}
// ANCHOR_END: docs_identity

impl PartialEq for Identity {
    fn eq(self, other: Self) -> bool {
        match (self, other) {
            (Identity::Address(addr1), Identity::Address(addr2)) => addr1 == addr2,
            (Identity::ContractId(id1), Identity::ContractId(id2)) => id1 == id2,
            _ => false,
        }
    }
}
impl Eq for Identity {}

impl Identity {
    /// Returns the `Address` of the `Identity`.
    ///
    /// # Returns
    ///
    /// * [Option<Address>] - `Some(Address)` if the underlying type is an `Address`, otherwise `None`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() {
    ///     let identity = Identity::Address(Address::zero());
    ///     let address = identity.as_address();
    ///     assert(address == Address::zero());
    /// }
    /// ```
    pub fn as_address(self) -> Option<Address> {
        match self {
            Self::Address(addr) => Some(addr),
            Self::ContractId(_) => None,
        }
    }

    /// Returns the `ContractId` of the `Identity`.
    ///
    /// # Returns
    ///
    /// * [Option<ContractId>] - `Some(Contract)` if the underlying type is an `ContractId`, otherwise `None`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() {
    ///     let identity = Identity::ContractId(ContractId::zero());
    ///     let contract_id = identity.as_contract_id();
    ///     assert(contract_id == ContractId::zero());
    /// }
    /// ```
    pub fn as_contract_id(self) -> Option<ContractId> {
        match self {
            Self::Address(_) => None,
            Self::ContractId(id) => Some(id),
        }
    }

    /// Returns whether the `Identity` represents an `Address`.
    ///
    /// # Returns
    ///
    /// * [bool] - Indicates whether the `Identity` holds an `Address`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() {
    ///     let identity = Identity::Address(Address::zero());
    ///     assert(identity.is_address());
    /// }
    /// ```
    pub fn is_address(self) -> bool {
        match self {
            Self::Address(_) => true,
            Self::ContractId(_) => false,
        }
    }

    /// Returns whether the `Identity` represents a `ContractId`.
    ///
    /// # Returns
    ///
    /// * [bool] - Indicates whether the `Identity` holds a `ContractId`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() {
    ///     let identity = Identity::ContractId(ContractId::zero());
    ///     assert(identity.is_contract_id());
    /// }
    /// ```
    pub fn is_contract_id(self) -> bool {
        match self {
            Self::Address(_) => false,
            Self::ContractId(_) => true,
        }
    }

    /// Returns the underlying raw `b256` data of the identity.
    ///
    /// # Returns
    ///
    /// * [b256] - The raw data of the identity.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() -> {
    ///     let my_identity = Identity::Address(Address::zero());
    ///     assert(my_identity.bits() == b256::zero());
    /// }
    /// ```
    pub fn bits(self) -> b256 {
        match self {
            Self::Address(address) => address.bits(),
            Self::ContractId(contract_id) => contract_id.bits(),
        }
    }
}

impl Hash for Identity {
    fn hash(self, ref mut state: Hasher) {
        match self {
            Identity::Address(address) => {
                0_u8.hash(state);
                address.hash(state);
            },
            Identity::ContractId(id) => {
                1_u8.hash(state);
                id.hash(state);
            },
        }
    }
}\n```

Casting to an Identity must be done explicitly:

```sway\ncontract;

mod r#abi;
mod errors;

use abi::IdentityExample;
use errors::MyError;

use std::asset::transfer;

storage {
    owner: Identity = Identity::ContractId(ContractId::zero()),
}

impl IdentityExample for Contract {
    fn cast_to_identity() {
        // ANCHOR: cast_to_identity
        let raw_address: b256 = 0xddec0e7e6a9a4a4e3e57d08d080d71a299c628a46bc609aab4627695679421ca;
        let my_identity: Identity = Identity::Address(Address::from(raw_address));
        // ANCHOR_END: cast_to_identity
    }

    fn identity_to_contract_id(my_identity: Identity) {
        // ANCHOR: identity_to_contract_id
        let my_contract_id: ContractId = match my_identity {
            Identity::ContractId(identity) => identity,
            _ => revert(0),
        };
        // ANCHOR_END: identity_to_contract_id
    }

    fn different_executions(my_identity: Identity) {
        // ANCHOR: different_executions
        match my_identity {
            Identity::Address(address) => takes_address(address),
            Identity::ContractId(contract_id) => takes_contract_id(contract_id),
        };
        // ANCHOR_END: different_executions
    }

    #[storage(read)]
    fn access_control_with_identity() {
        // ANCHOR: access_control_with_identity
        let sender = msg_sender().unwrap();
        require(
            sender == storage
                .owner
                .read(),
            MyError::UnauthorizedUser(sender),
        );
        // ANCHOR_END: access_control_with_identity
    }
}

fn takes_address(address: Address) {}

fn takes_contract_id(contract_id: ContractId) {}\n```

A match statement can be used to return to an Address or ContractId as well as handle cases in which their execution differs.

```sway\ncontract;

mod r#abi;
mod errors;

use abi::IdentityExample;
use errors::MyError;

use std::asset::transfer;

storage {
    owner: Identity = Identity::ContractId(ContractId::zero()),
}

impl IdentityExample for Contract {
    fn cast_to_identity() {
        // ANCHOR: cast_to_identity
        let raw_address: b256 = 0xddec0e7e6a9a4a4e3e57d08d080d71a299c628a46bc609aab4627695679421ca;
        let my_identity: Identity = Identity::Address(Address::from(raw_address));
        // ANCHOR_END: cast_to_identity
    }

    fn identity_to_contract_id(my_identity: Identity) {
        // ANCHOR: identity_to_contract_id
        let my_contract_id: ContractId = match my_identity {
            Identity::ContractId(identity) => identity,
            _ => revert(0),
        };
        // ANCHOR_END: identity_to_contract_id
    }

    fn different_executions(my_identity: Identity) {
        // ANCHOR: different_executions
        match my_identity {
            Identity::Address(address) => takes_address(address),
            Identity::ContractId(contract_id) => takes_contract_id(contract_id),
        };
        // ANCHOR_END: different_executions
    }

    #[storage(read)]
    fn access_control_with_identity() {
        // ANCHOR: access_control_with_identity
        let sender = msg_sender().unwrap();
        require(
            sender == storage
                .owner
                .read(),
            MyError::UnauthorizedUser(sender),
        );
        // ANCHOR_END: access_control_with_identity
    }
}

fn takes_address(address: Address) {}

fn takes_contract_id(contract_id: ContractId) {}\n```
```sway\ncontract;

mod r#abi;
mod errors;

use abi::IdentityExample;
use errors::MyError;

use std::asset::transfer;

storage {
    owner: Identity = Identity::ContractId(ContractId::zero()),
}

impl IdentityExample for Contract {
    fn cast_to_identity() {
        // ANCHOR: cast_to_identity
        let raw_address: b256 = 0xddec0e7e6a9a4a4e3e57d08d080d71a299c628a46bc609aab4627695679421ca;
        let my_identity: Identity = Identity::Address(Address::from(raw_address));
        // ANCHOR_END: cast_to_identity
    }

    fn identity_to_contract_id(my_identity: Identity) {
        // ANCHOR: identity_to_contract_id
        let my_contract_id: ContractId = match my_identity {
            Identity::ContractId(identity) => identity,
            _ => revert(0),
        };
        // ANCHOR_END: identity_to_contract_id
    }

    fn different_executions(my_identity: Identity) {
        // ANCHOR: different_executions
        match my_identity {
            Identity::Address(address) => takes_address(address),
            Identity::ContractId(contract_id) => takes_contract_id(contract_id),
        };
        // ANCHOR_END: different_executions
    }

    #[storage(read)]
    fn access_control_with_identity() {
        // ANCHOR: access_control_with_identity
        let sender = msg_sender().unwrap();
        require(
            sender == storage
                .owner
                .read(),
            MyError::UnauthorizedUser(sender),
        );
        // ANCHOR_END: access_control_with_identity
    }
}

fn takes_address(address: Address) {}

fn takes_contract_id(contract_id: ContractId) {}\n```

A common use case for Identity is for access control. The use of Identity uniquely allows both ContractId and Address to have access control inclusively.

```sway\ncontract;

mod r#abi;
mod errors;

use abi::IdentityExample;
use errors::MyError;

use std::asset::transfer;

storage {
    owner: Identity = Identity::ContractId(ContractId::zero()),
}

impl IdentityExample for Contract {
    fn cast_to_identity() {
        // ANCHOR: cast_to_identity
        let raw_address: b256 = 0xddec0e7e6a9a4a4e3e57d08d080d71a299c628a46bc609aab4627695679421ca;
        let my_identity: Identity = Identity::Address(Address::from(raw_address));
        // ANCHOR_END: cast_to_identity
    }

    fn identity_to_contract_id(my_identity: Identity) {
        // ANCHOR: identity_to_contract_id
        let my_contract_id: ContractId = match my_identity {
            Identity::ContractId(identity) => identity,
            _ => revert(0),
        };
        // ANCHOR_END: identity_to_contract_id
    }

    fn different_executions(my_identity: Identity) {
        // ANCHOR: different_executions
        match my_identity {
            Identity::Address(address) => takes_address(address),
            Identity::ContractId(contract_id) => takes_contract_id(contract_id),
        };
        // ANCHOR_END: different_executions
    }

    #[storage(read)]
    fn access_control_with_identity() {
        // ANCHOR: access_control_with_identity
        let sender = msg_sender().unwrap();
        require(
            sender == storage
                .owner
                .read(),
            MyError::UnauthorizedUser(sender),
        );
        // ANCHOR_END: access_control_with_identity
    }
}

fn takes_address(address: Address) {}

fn takes_contract_id(contract_id: ContractId) {}\n```

Converting Types

Below are some common type conversions in Sway:

Identity Conversions

Convert to Identity

```sway\nlibrary;

pub fn convert_to_identity() {
    let b256_address: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    // ANCHOR: convert_b256_to_address_or_contract_id
    let address_from_b256: Address = Address::from(b256_address);
    let contract_id_from_b256: ContractId = ContractId::from(b256_address);
    // ANCHOR_END: convert_b256_to_address_or_contract_id
    let address = address_from_b256;
    let contract_id = contract_id_from_b256;

    // ANCHOR: convert_to_identity
    let identity_from_b256: Identity = Identity::Address(Address::from(b256_address));
    let identity_from_address: Identity = Identity::Address(address);
    let identity_from_contract_id: Identity = Identity::ContractId(contract_id);
    // ANCHOR_END: convert_to_identity
}

pub fn convert_from_identity(my_identity: Identity) {
    // ANCHOR: convert_from_identity
    match my_identity {
        Identity::Address(address) => log(address),
        Identity::ContractId(contract_id) => log(contract_id),
    };
    // ANCHOR_END: convert_from_identity
}

pub fn convert_to_b256() {
    let b256_address: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    let address: Address = Address::from(b256_address);
    let contract_id: ContractId = ContractId::from(b256_address);

    // ANCHOR: convert_to_b256
    let b256_from_address: b256 = address.into();
    let b256_from_contract_id: b256 = contract_id.into();
    // ANCHOR_END: convert_to_b256
}\n```

Convert Identity to ContractId or Address

```sway\nlibrary;

pub fn convert_to_identity() {
    let b256_address: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    // ANCHOR: convert_b256_to_address_or_contract_id
    let address_from_b256: Address = Address::from(b256_address);
    let contract_id_from_b256: ContractId = ContractId::from(b256_address);
    // ANCHOR_END: convert_b256_to_address_or_contract_id
    let address = address_from_b256;
    let contract_id = contract_id_from_b256;

    // ANCHOR: convert_to_identity
    let identity_from_b256: Identity = Identity::Address(Address::from(b256_address));
    let identity_from_address: Identity = Identity::Address(address);
    let identity_from_contract_id: Identity = Identity::ContractId(contract_id);
    // ANCHOR_END: convert_to_identity
}

pub fn convert_from_identity(my_identity: Identity) {
    // ANCHOR: convert_from_identity
    match my_identity {
        Identity::Address(address) => log(address),
        Identity::ContractId(contract_id) => log(contract_id),
    };
    // ANCHOR_END: convert_from_identity
}

pub fn convert_to_b256() {
    let b256_address: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    let address: Address = Address::from(b256_address);
    let contract_id: ContractId = ContractId::from(b256_address);

    // ANCHOR: convert_to_b256
    let b256_from_address: b256 = address.into();
    let b256_from_contract_id: b256 = contract_id.into();
    // ANCHOR_END: convert_to_b256
}\n```

Convert ContractId or Address to b256

```sway\nlibrary;

pub fn convert_to_identity() {
    let b256_address: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    // ANCHOR: convert_b256_to_address_or_contract_id
    let address_from_b256: Address = Address::from(b256_address);
    let contract_id_from_b256: ContractId = ContractId::from(b256_address);
    // ANCHOR_END: convert_b256_to_address_or_contract_id
    let address = address_from_b256;
    let contract_id = contract_id_from_b256;

    // ANCHOR: convert_to_identity
    let identity_from_b256: Identity = Identity::Address(Address::from(b256_address));
    let identity_from_address: Identity = Identity::Address(address);
    let identity_from_contract_id: Identity = Identity::ContractId(contract_id);
    // ANCHOR_END: convert_to_identity
}

pub fn convert_from_identity(my_identity: Identity) {
    // ANCHOR: convert_from_identity
    match my_identity {
        Identity::Address(address) => log(address),
        Identity::ContractId(contract_id) => log(contract_id),
    };
    // ANCHOR_END: convert_from_identity
}

pub fn convert_to_b256() {
    let b256_address: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    let address: Address = Address::from(b256_address);
    let contract_id: ContractId = ContractId::from(b256_address);

    // ANCHOR: convert_to_b256
    let b256_from_address: b256 = address.into();
    let b256_from_contract_id: b256 = contract_id.into();
    // ANCHOR_END: convert_to_b256
}\n```

Convert b256 to ContractId or Address

```sway\nlibrary;

pub fn convert_to_identity() {
    let b256_address: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    // ANCHOR: convert_b256_to_address_or_contract_id
    let address_from_b256: Address = Address::from(b256_address);
    let contract_id_from_b256: ContractId = ContractId::from(b256_address);
    // ANCHOR_END: convert_b256_to_address_or_contract_id
    let address = address_from_b256;
    let contract_id = contract_id_from_b256;

    // ANCHOR: convert_to_identity
    let identity_from_b256: Identity = Identity::Address(Address::from(b256_address));
    let identity_from_address: Identity = Identity::Address(address);
    let identity_from_contract_id: Identity = Identity::ContractId(contract_id);
    // ANCHOR_END: convert_to_identity
}

pub fn convert_from_identity(my_identity: Identity) {
    // ANCHOR: convert_from_identity
    match my_identity {
        Identity::Address(address) => log(address),
        Identity::ContractId(contract_id) => log(contract_id),
    };
    // ANCHOR_END: convert_from_identity
}

pub fn convert_to_b256() {
    let b256_address: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    let address: Address = Address::from(b256_address);
    let contract_id: ContractId = ContractId::from(b256_address);

    // ANCHOR: convert_to_b256
    let b256_from_address: b256 = address.into();
    let b256_from_contract_id: b256 = contract_id.into();
    // ANCHOR_END: convert_to_b256
}\n```

String Conversions

Convert str to str[]

```sway\nlibrary;

pub fn convert_str_to_str_array() {
    // ANCHOR: str_to_str_array
    let fuel_str: str = "fuel";
    let fuel_str_array: str[4] = fuel_str.try_as_str_array().unwrap();
    // ANCHOR_END: str_to_str_array
}

pub fn convert_str_array_to_str() {
    // ANCHOR: str_array_to_str
    let fuel_str_array: str[4] = __to_str_array("fuel");
    let fuel_str: str = from_str_array(fuel_str_array);
    // ANCHOR_END: str_array_to_str
}\n```

Convert str[] to str

```sway\nlibrary;

pub fn convert_str_to_str_array() {
    // ANCHOR: str_to_str_array
    let fuel_str: str = "fuel";
    let fuel_str_array: str[4] = fuel_str.try_as_str_array().unwrap();
    // ANCHOR_END: str_to_str_array
}

pub fn convert_str_array_to_str() {
    // ANCHOR: str_array_to_str
    let fuel_str_array: str[4] = __to_str_array("fuel");
    let fuel_str: str = from_str_array(fuel_str_array);
    // ANCHOR_END: str_array_to_str
}\n```

Number Conversions

Convert to u256

```sway\nlibrary;

pub fn convert_to_u256() {
    // Convert any unsigned integer to `u256`
    // ANCHOR: to_u256
    let u8_1: u8 = 2u8;
    let u16_1: u16 = 2u16;
    let u32_1: u32 = 2u32;
    let u64_1: u64 = 2u64;
    let b256_1: b256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20;

    let u256_from_u8: u256 = u8_1.as_u256();
    let u256_from_u16: u256 = u16_1.as_u256();
    let u256_from_u32: u256 = u32_1.as_u256();
    let u256_from_u64: u256 = u64_1.as_u256();
    let u256_from_b256: u256 = b256_1.as_u256();
    // ANCHOR_END: to_u256
}\n```

Convert to u64

```sway\nlibrary;

pub fn convert_uint_to_u64() {
    // Convert any unsigned integer to `u64`
    // ANCHOR: to_u64
    let u8_1: u8 = 2u8;
    let u16_1: u16 = 2u16;
    let u32_1: u32 = 2u32;
    let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256;

    let u64_from_u8: u64 = u8_1.as_u64();

    let u64_from_u16: u64 = u16_1.as_u64();

    let u64_from_u32: u64 = u32_1.as_u64();

    let u64_from_u256: Option<u64> = <u64 as TryFrom<u256>>::try_from(u256_1);
    // ANCHOR_END: to_u64
}\n```

Convert to u32

```sway\nlibrary;

pub fn convert_uint_to_u32() {
    // Convert any unsigned integer to `u32`
    // ANCHOR: to_u32
    let u8_1: u8 = 2u8;
    let u16_1: u16 = 2u16;
    let u64_1: u64 = 2;
    let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256;

    let u32_from_u8: u32 = u8_1.as_u32();

    let u32_from_u16: u32 = u16_1.as_u32();

    let u32_from_u64_1: Option<u32> = u64_1.try_as_u32();
    let u32_from_u64_2: Option<u32> = <u32 as TryFrom<u64>>::try_from(u64_1);

    let u32_from_u256: Option<u32> = <u32 as TryFrom<u256>>::try_from(u256_1);
    // ANCHOR_END: to_u32
}\n```

Convert to u16

```sway\nlibrary;

pub fn convert_uint_to_u16() {
    // Convert any unsigned integer to `u16`
    // ANCHOR: to_u16
    let u8_1: u8 = 2u8;
    let u32_1: u32 = 2u32;
    let u64_1: u64 = 2;
    let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256;

    let u16_from_u8: u16 = u8_1.as_u16();

    let u16_from_u32_1: Option<u16> = u32_1.try_as_u16();
    let u16_from_u32_2: Option<u16> = <u16 as TryFrom<u32>>::try_from(u32_1);

    let u16_from_u64_1: Option<u16> = u64_1.try_as_u16();
    let u16_from_u64_2: Option<u16> = <u16 as TryFrom<u64>>::try_from(u64_1);

    let u16_from_u256: Option<u16> = <u16 as TryFrom<u256>>::try_from(u256_1);
    // ANCHOR_END: to_u16
}\n```

Convert to u8

```sway\nlibrary;

pub fn convert_uint_to_u8() {
    // Convert any unsigned integer to `u8`
    // ANCHOR: to_u8
    let u16_1: u16 = 2u16;
    let u32_1: u32 = 2u32;
    let u64_1: u64 = 2;
    let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256;

    let u8_from_u16_1: Option<u8> = u16_1.try_as_u8();
    let u8_from_u16_2: Option<u8> = <u8 as TryFrom<u16>>::try_from(u16_1);

    let u8_from_u32_1: Option<u8> = u32_1.try_as_u8();
    let u8_from_u32_2: Option<u8> = <u8 as TryFrom<u32>>::try_from(u32_1);

    let u8_from_u64_1: Option<u8> = u64_1.try_as_u8();
    let u8_from_u64_2: Option<u8> = <u8 as TryFrom<u64>>::try_from(u64_1);

    let u8_from_u256: Option<u8> = <u8 as TryFrom<u256>>::try_from(u256_1);
    // ANCHOR_END: to_u8
    assert(u8_from_u16_1.unwrap() == 2u8);
    assert(u8_from_u16_2.unwrap() == 2u8);
    assert(u8_from_u32_1.unwrap() == 2u8);
    assert(u8_from_u32_2.unwrap() == 2u8);
    assert(u8_from_u64_1.unwrap() == 2u8);
    assert(u8_from_u64_2.unwrap() == 2u8);
    assert(u8_from_u256.unwrap() == 2u8);
}\n```

Convert to Bytes

```sway\nlibrary;

// ANCHOR: to_bytes_import
use std::{bytes::Bytes, bytes_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*}};
// ANCHOR_END: to_bytes_import

pub fn convert_to_bytes() {
    // Convert any unsigned integeger to `Bytes`
    // ANCHOR: to_bytes
    let num = 5;
    let little_endian_bytes: Bytes = num.to_le_bytes();
    let big_endian_bytes: Bytes = num.to_be_bytes();
    // ANCHOR_END: to_bytes
}

pub fn convert_from_bytes() {
    let num = 5;
    let little_endian_bytes: Bytes = num.to_le_bytes();
    let big_endian_bytes: Bytes = num.to_be_bytes();

    // Convert `Bytes` to an unsigned integeger
    // ANCHOR: from_bytes
    let u16_from_le_bytes: u16 = u16::from_le_bytes(little_endian_bytes);
    let u16_from_be_bytes: u16 = u16::from_be_bytes(big_endian_bytes);

    let u32_from_le_bytes: u32 = u32::from_le_bytes(little_endian_bytes);
    let u32_from_be_bytes: u32 = u32::from_be_bytes(big_endian_bytes);

    let u64_from_le_bytes: u64 = u64::from_le_bytes(little_endian_bytes);
    let u64_from_be_bytes: u64 = u64::from_be_bytes(big_endian_bytes);

    let u256_from_le_bytes = u256::from_le_bytes(little_endian_bytes);
    let u256_from_be_bytes = u256::from_be_bytes(big_endian_bytes);

    let b256_from_le_bytes = b256::from_le_bytes(little_endian_bytes);
    let b256_from_be_bytes = b256::from_be_bytes(big_endian_bytes);
    // ANCHOR_END: from_bytes
}\n```
```sway\nlibrary;

// ANCHOR: to_bytes_import
use std::{bytes::Bytes, bytes_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*}};
// ANCHOR_END: to_bytes_import

pub fn convert_to_bytes() {
    // Convert any unsigned integeger to `Bytes`
    // ANCHOR: to_bytes
    let num = 5;
    let little_endian_bytes: Bytes = num.to_le_bytes();
    let big_endian_bytes: Bytes = num.to_be_bytes();
    // ANCHOR_END: to_bytes
}

pub fn convert_from_bytes() {
    let num = 5;
    let little_endian_bytes: Bytes = num.to_le_bytes();
    let big_endian_bytes: Bytes = num.to_be_bytes();

    // Convert `Bytes` to an unsigned integeger
    // ANCHOR: from_bytes
    let u16_from_le_bytes: u16 = u16::from_le_bytes(little_endian_bytes);
    let u16_from_be_bytes: u16 = u16::from_be_bytes(big_endian_bytes);

    let u32_from_le_bytes: u32 = u32::from_le_bytes(little_endian_bytes);
    let u32_from_be_bytes: u32 = u32::from_be_bytes(big_endian_bytes);

    let u64_from_le_bytes: u64 = u64::from_le_bytes(little_endian_bytes);
    let u64_from_be_bytes: u64 = u64::from_be_bytes(big_endian_bytes);

    let u256_from_le_bytes = u256::from_le_bytes(little_endian_bytes);
    let u256_from_be_bytes = u256::from_be_bytes(big_endian_bytes);

    let b256_from_le_bytes = b256::from_le_bytes(little_endian_bytes);
    let b256_from_be_bytes = b256::from_be_bytes(big_endian_bytes);
    // ANCHOR_END: from_bytes
}\n```

Convert from Bytes

```sway\nlibrary;

// ANCHOR: to_bytes_import
use std::{bytes::Bytes, bytes_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*}};
// ANCHOR_END: to_bytes_import

pub fn convert_to_bytes() {
    // Convert any unsigned integeger to `Bytes`
    // ANCHOR: to_bytes
    let num = 5;
    let little_endian_bytes: Bytes = num.to_le_bytes();
    let big_endian_bytes: Bytes = num.to_be_bytes();
    // ANCHOR_END: to_bytes
}

pub fn convert_from_bytes() {
    let num = 5;
    let little_endian_bytes: Bytes = num.to_le_bytes();
    let big_endian_bytes: Bytes = num.to_be_bytes();

    // Convert `Bytes` to an unsigned integeger
    // ANCHOR: from_bytes
    let u16_from_le_bytes: u16 = u16::from_le_bytes(little_endian_bytes);
    let u16_from_be_bytes: u16 = u16::from_be_bytes(big_endian_bytes);

    let u32_from_le_bytes: u32 = u32::from_le_bytes(little_endian_bytes);
    let u32_from_be_bytes: u32 = u32::from_be_bytes(big_endian_bytes);

    let u64_from_le_bytes: u64 = u64::from_le_bytes(little_endian_bytes);
    let u64_from_be_bytes: u64 = u64::from_be_bytes(big_endian_bytes);

    let u256_from_le_bytes = u256::from_le_bytes(little_endian_bytes);
    let u256_from_be_bytes = u256::from_be_bytes(big_endian_bytes);

    let b256_from_le_bytes = b256::from_le_bytes(little_endian_bytes);
    let b256_from_be_bytes = b256::from_be_bytes(big_endian_bytes);
    // ANCHOR_END: from_bytes
}\n```
```sway\nlibrary;

// ANCHOR: to_bytes_import
use std::{bytes::Bytes, bytes_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*}};
// ANCHOR_END: to_bytes_import

pub fn convert_to_bytes() {
    // Convert any unsigned integeger to `Bytes`
    // ANCHOR: to_bytes
    let num = 5;
    let little_endian_bytes: Bytes = num.to_le_bytes();
    let big_endian_bytes: Bytes = num.to_be_bytes();
    // ANCHOR_END: to_bytes
}

pub fn convert_from_bytes() {
    let num = 5;
    let little_endian_bytes: Bytes = num.to_le_bytes();
    let big_endian_bytes: Bytes = num.to_be_bytes();

    // Convert `Bytes` to an unsigned integeger
    // ANCHOR: from_bytes
    let u16_from_le_bytes: u16 = u16::from_le_bytes(little_endian_bytes);
    let u16_from_be_bytes: u16 = u16::from_be_bytes(big_endian_bytes);

    let u32_from_le_bytes: u32 = u32::from_le_bytes(little_endian_bytes);
    let u32_from_be_bytes: u32 = u32::from_be_bytes(big_endian_bytes);

    let u64_from_le_bytes: u64 = u64::from_le_bytes(little_endian_bytes);
    let u64_from_be_bytes: u64 = u64::from_be_bytes(big_endian_bytes);

    let u256_from_le_bytes = u256::from_le_bytes(little_endian_bytes);
    let u256_from_be_bytes = u256::from_be_bytes(big_endian_bytes);

    let b256_from_le_bytes = b256::from_le_bytes(little_endian_bytes);
    let b256_from_be_bytes = b256::from_be_bytes(big_endian_bytes);
    // ANCHOR_END: from_bytes
}\n```

Byte Array Conversions

Convert to a Byte Array

```sway\nlibrary;

// ANCHOR: to_byte_array_import
use std::array_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*};
// ANCHOR_END: to_byte_array_import

pub fn to_byte_array() {
    // ANCHOR: to_byte_array
    let u16_1: u16 = 2u16;
    let u32_1: u32 = 2u32;
    let u64_1: u64 = 2u64;
    let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256;
    let b256_1: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    // little endian
    let le_byte_array_from_u16: [u8; 2] = u16_1.to_le_bytes();
    let le_byte_array_from_u32: [u8; 4] = u32_1.to_le_bytes();
    let le_byte_array_from_u64: [u8; 8] = u64_1.to_le_bytes();
    let le_byte_array_from_u256: [u8; 32] = u256_1.to_le_bytes();
    let le_byte_array_from_b256: [u8; 32] = b256_1.to_le_bytes();
    // big endian
    let be_byte_array_from_u16: [u8; 2] = u16_1.to_be_bytes();
    let be_byte_array_from_u32: [u8; 4] = u32_1.to_be_bytes();
    let be_byte_array_from_u64: [u8; 8] = u64_1.to_be_bytes();
    let be_byte_array_from_u256: [u8; 32] = u256_1.to_be_bytes();
    let be_byte_array_from_b256: [u8; 32] = b256_1.to_be_bytes();
    // ANCHOR_END: to_byte_array
}
pub fn from_byte_array() {
    // ANCHOR: from_byte_array
    let u16_byte_array: [u8; 2] = [2_u8, 1_u8];
    let u32_byte_array: [u8; 4] = [4_u8, 3_u8, 2_u8, 1_u8];
    let u64_byte_array: [u8; 8] = [8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, 2_u8, 1_u8];
    let u256_byte_array: [u8; 32] = [
        32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, 22_u8,
        21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, 12_u8, 11_u8,
        10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, 2_u8, 1_u8,
    ];
    // little endian
    let le_u16_from_byte_array: u16 = u16::from_le_bytes(u16_byte_array);
    let le_u32_from_byte_array: u32 = u32::from_le_bytes(u32_byte_array);
    let le_u64_from_byte_array: u64 = u64::from_le_bytes(u64_byte_array);
    let le_u256_from_byte_array: u256 = u256::from_le_bytes(u256_byte_array);
    let le_b256_from_byte_array: b256 = b256::from_le_bytes(u256_byte_array);
    // big endian
    let be_u16_from_byte_array: u16 = u16::from_be_bytes(u16_byte_array);
    let be_u32_from_byte_array: u32 = u32::from_be_bytes(u32_byte_array);
    let be_u64_from_byte_array: u64 = u64::from_be_bytes(u64_byte_array);
    let be_u256_from_byte_array: u256 = u256::from_be_bytes(u256_byte_array);
    let be_b256_from_byte_array: b256 = b256::from_be_bytes(u256_byte_array);
    // ANCHOR_END: from_byte_array
}\n```
```sway\nlibrary;

// ANCHOR: to_byte_array_import
use std::array_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*};
// ANCHOR_END: to_byte_array_import

pub fn to_byte_array() {
    // ANCHOR: to_byte_array
    let u16_1: u16 = 2u16;
    let u32_1: u32 = 2u32;
    let u64_1: u64 = 2u64;
    let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256;
    let b256_1: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    // little endian
    let le_byte_array_from_u16: [u8; 2] = u16_1.to_le_bytes();
    let le_byte_array_from_u32: [u8; 4] = u32_1.to_le_bytes();
    let le_byte_array_from_u64: [u8; 8] = u64_1.to_le_bytes();
    let le_byte_array_from_u256: [u8; 32] = u256_1.to_le_bytes();
    let le_byte_array_from_b256: [u8; 32] = b256_1.to_le_bytes();
    // big endian
    let be_byte_array_from_u16: [u8; 2] = u16_1.to_be_bytes();
    let be_byte_array_from_u32: [u8; 4] = u32_1.to_be_bytes();
    let be_byte_array_from_u64: [u8; 8] = u64_1.to_be_bytes();
    let be_byte_array_from_u256: [u8; 32] = u256_1.to_be_bytes();
    let be_byte_array_from_b256: [u8; 32] = b256_1.to_be_bytes();
    // ANCHOR_END: to_byte_array
}
pub fn from_byte_array() {
    // ANCHOR: from_byte_array
    let u16_byte_array: [u8; 2] = [2_u8, 1_u8];
    let u32_byte_array: [u8; 4] = [4_u8, 3_u8, 2_u8, 1_u8];
    let u64_byte_array: [u8; 8] = [8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, 2_u8, 1_u8];
    let u256_byte_array: [u8; 32] = [
        32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, 22_u8,
        21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, 12_u8, 11_u8,
        10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, 2_u8, 1_u8,
    ];
    // little endian
    let le_u16_from_byte_array: u16 = u16::from_le_bytes(u16_byte_array);
    let le_u32_from_byte_array: u32 = u32::from_le_bytes(u32_byte_array);
    let le_u64_from_byte_array: u64 = u64::from_le_bytes(u64_byte_array);
    let le_u256_from_byte_array: u256 = u256::from_le_bytes(u256_byte_array);
    let le_b256_from_byte_array: b256 = b256::from_le_bytes(u256_byte_array);
    // big endian
    let be_u16_from_byte_array: u16 = u16::from_be_bytes(u16_byte_array);
    let be_u32_from_byte_array: u32 = u32::from_be_bytes(u32_byte_array);
    let be_u64_from_byte_array: u64 = u64::from_be_bytes(u64_byte_array);
    let be_u256_from_byte_array: u256 = u256::from_be_bytes(u256_byte_array);
    let be_b256_from_byte_array: b256 = b256::from_be_bytes(u256_byte_array);
    // ANCHOR_END: from_byte_array
}\n```

Convert from a Byte Array

```sway\nlibrary;

// ANCHOR: to_byte_array_import
use std::array_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*};
// ANCHOR_END: to_byte_array_import

pub fn to_byte_array() {
    // ANCHOR: to_byte_array
    let u16_1: u16 = 2u16;
    let u32_1: u32 = 2u32;
    let u64_1: u64 = 2u64;
    let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256;
    let b256_1: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    // little endian
    let le_byte_array_from_u16: [u8; 2] = u16_1.to_le_bytes();
    let le_byte_array_from_u32: [u8; 4] = u32_1.to_le_bytes();
    let le_byte_array_from_u64: [u8; 8] = u64_1.to_le_bytes();
    let le_byte_array_from_u256: [u8; 32] = u256_1.to_le_bytes();
    let le_byte_array_from_b256: [u8; 32] = b256_1.to_le_bytes();
    // big endian
    let be_byte_array_from_u16: [u8; 2] = u16_1.to_be_bytes();
    let be_byte_array_from_u32: [u8; 4] = u32_1.to_be_bytes();
    let be_byte_array_from_u64: [u8; 8] = u64_1.to_be_bytes();
    let be_byte_array_from_u256: [u8; 32] = u256_1.to_be_bytes();
    let be_byte_array_from_b256: [u8; 32] = b256_1.to_be_bytes();
    // ANCHOR_END: to_byte_array
}
pub fn from_byte_array() {
    // ANCHOR: from_byte_array
    let u16_byte_array: [u8; 2] = [2_u8, 1_u8];
    let u32_byte_array: [u8; 4] = [4_u8, 3_u8, 2_u8, 1_u8];
    let u64_byte_array: [u8; 8] = [8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, 2_u8, 1_u8];
    let u256_byte_array: [u8; 32] = [
        32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, 22_u8,
        21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, 12_u8, 11_u8,
        10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, 2_u8, 1_u8,
    ];
    // little endian
    let le_u16_from_byte_array: u16 = u16::from_le_bytes(u16_byte_array);
    let le_u32_from_byte_array: u32 = u32::from_le_bytes(u32_byte_array);
    let le_u64_from_byte_array: u64 = u64::from_le_bytes(u64_byte_array);
    let le_u256_from_byte_array: u256 = u256::from_le_bytes(u256_byte_array);
    let le_b256_from_byte_array: b256 = b256::from_le_bytes(u256_byte_array);
    // big endian
    let be_u16_from_byte_array: u16 = u16::from_be_bytes(u16_byte_array);
    let be_u32_from_byte_array: u32 = u32::from_be_bytes(u32_byte_array);
    let be_u64_from_byte_array: u64 = u64::from_be_bytes(u64_byte_array);
    let be_u256_from_byte_array: u256 = u256::from_be_bytes(u256_byte_array);
    let be_b256_from_byte_array: b256 = b256::from_be_bytes(u256_byte_array);
    // ANCHOR_END: from_byte_array
}\n```
```sway\nlibrary;

// ANCHOR: to_byte_array_import
use std::array_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*};
// ANCHOR_END: to_byte_array_import

pub fn to_byte_array() {
    // ANCHOR: to_byte_array
    let u16_1: u16 = 2u16;
    let u32_1: u32 = 2u32;
    let u64_1: u64 = 2u64;
    let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256;
    let b256_1: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
    // little endian
    let le_byte_array_from_u16: [u8; 2] = u16_1.to_le_bytes();
    let le_byte_array_from_u32: [u8; 4] = u32_1.to_le_bytes();
    let le_byte_array_from_u64: [u8; 8] = u64_1.to_le_bytes();
    let le_byte_array_from_u256: [u8; 32] = u256_1.to_le_bytes();
    let le_byte_array_from_b256: [u8; 32] = b256_1.to_le_bytes();
    // big endian
    let be_byte_array_from_u16: [u8; 2] = u16_1.to_be_bytes();
    let be_byte_array_from_u32: [u8; 4] = u32_1.to_be_bytes();
    let be_byte_array_from_u64: [u8; 8] = u64_1.to_be_bytes();
    let be_byte_array_from_u256: [u8; 32] = u256_1.to_be_bytes();
    let be_byte_array_from_b256: [u8; 32] = b256_1.to_be_bytes();
    // ANCHOR_END: to_byte_array
}
pub fn from_byte_array() {
    // ANCHOR: from_byte_array
    let u16_byte_array: [u8; 2] = [2_u8, 1_u8];
    let u32_byte_array: [u8; 4] = [4_u8, 3_u8, 2_u8, 1_u8];
    let u64_byte_array: [u8; 8] = [8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, 2_u8, 1_u8];
    let u256_byte_array: [u8; 32] = [
        32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, 22_u8,
        21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, 12_u8, 11_u8,
        10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, 2_u8, 1_u8,
    ];
    // little endian
    let le_u16_from_byte_array: u16 = u16::from_le_bytes(u16_byte_array);
    let le_u32_from_byte_array: u32 = u32::from_le_bytes(u32_byte_array);
    let le_u64_from_byte_array: u64 = u64::from_le_bytes(u64_byte_array);
    let le_u256_from_byte_array: u256 = u256::from_le_bytes(u256_byte_array);
    let le_b256_from_byte_array: b256 = b256::from_le_bytes(u256_byte_array);
    // big endian
    let be_u16_from_byte_array: u16 = u16::from_be_bytes(u16_byte_array);
    let be_u32_from_byte_array: u32 = u32::from_be_bytes(u32_byte_array);
    let be_u64_from_byte_array: u64 = u64::from_be_bytes(u64_byte_array);
    let be_u256_from_byte_array: u256 = u256::from_be_bytes(u256_byte_array);
    let be_b256_from_byte_array: b256 = b256::from_be_bytes(u256_byte_array);
    // ANCHOR_END: from_byte_array
}\n```

Functions

Functions in Sway are declared with the fn keyword. Let's take a look:

fn equals(first_param: u64, second_param: u64) -> bool {
    first_param == second_param
}

We have just declared a function named equals which takes two parameters: first_param and second_param. The parameters must both be 64-bit unsigned integers.

This function also returns a bool value, i.e. either true or false. This function returns true if the two given parameters are equal, and false if they are not. If we want to use this function, we can do so like this:

fn main() {
    equals(5, 5); // evaluates to `true`
    equals(5, 6); // evaluates to `false`
}

Mutable Parameters

We can make a function parameter mutable by adding ref mut before the parameter name. This allows mutating the argument passed into the function when the function is called.

For example:

```sway\nscript;

enum Color {
    Red: (),
    Blue: (),
}

// ANCHOR: increment
fn increment(ref mut num: u32) {
    let prev = num;
    num = prev + 1u32;
}
// ANCHOR_END: increment
// ANCHOR: tuple_and_enum
fn swap_tuple(ref mut pair: (u64, u64)) {
    let temp = pair.0;
    pair.0 = pair.1;
    pair.1 = temp;
}

fn update_color(ref mut color: Color, new_color: Color) {
    color = new_color;
}
// ANCHOR_END: tuple_and_enum
// ANCHOR: move_right
struct Coordinates {
    x: u64,
    y: u64,
}

impl Coordinates {
    fn move_right(ref mut self, distance: u64) {
        self.x += distance;
    }
}
// ANCHOR_END: move_right
fn main() {
    // ANCHOR: call_increment
    let mut num: u32 = 0;
    increment(num);
    assert(num == 1u32); // The function `increment()` modifies `num`
    // ANCHOR_END: call_increment
    // ANCHOR: call_tuple_and_enum
    let mut tuple = (42, 24);
    swap_tuple(tuple);
    assert(tuple.0 == 24); // The function `swap_tuple()` modifies `tuple.0`
    assert(tuple.1 == 42); // The function `swap_tuple()` modifies `tuple.1`
    let mut color = Color::Red;
    update_color(color, Color::Blue);
    assert(match color {
        Color::Blue => true,
        _ => false,
    }); // The function `update_color()` modifies the color to Blue
    // ANCHOR_END: call_tuple_and_enum
    // ANCHOR: call_move_right
    let mut point = Coordinates { x: 1, y: 1 };
    point.move_right(5);
    assert(point.x == 6);
    assert(point.y == 1);
    // ANCHOR_END: call_move_right
}\n```

This function is allowed to mutate its parameter num because of the mut keyword. In addition, the ref keyword instructs the function to modify the argument passed to it when the function is called, instead of modifying a local copy of it.

```sway\nscript;

enum Color {
    Red: (),
    Blue: (),
}

// ANCHOR: increment
fn increment(ref mut num: u32) {
    let prev = num;
    num = prev + 1u32;
}
// ANCHOR_END: increment
// ANCHOR: tuple_and_enum
fn swap_tuple(ref mut pair: (u64, u64)) {
    let temp = pair.0;
    pair.0 = pair.1;
    pair.1 = temp;
}

fn update_color(ref mut color: Color, new_color: Color) {
    color = new_color;
}
// ANCHOR_END: tuple_and_enum
// ANCHOR: move_right
struct Coordinates {
    x: u64,
    y: u64,
}

impl Coordinates {
    fn move_right(ref mut self, distance: u64) {
        self.x += distance;
    }
}
// ANCHOR_END: move_right
fn main() {
    // ANCHOR: call_increment
    let mut num: u32 = 0;
    increment(num);
    assert(num == 1u32); // The function `increment()` modifies `num`
    // ANCHOR_END: call_increment
    // ANCHOR: call_tuple_and_enum
    let mut tuple = (42, 24);
    swap_tuple(tuple);
    assert(tuple.0 == 24); // The function `swap_tuple()` modifies `tuple.0`
    assert(tuple.1 == 42); // The function `swap_tuple()` modifies `tuple.1`
    let mut color = Color::Red;
    update_color(color, Color::Blue);
    assert(match color {
        Color::Blue => true,
        _ => false,
    }); // The function `update_color()` modifies the color to Blue
    // ANCHOR_END: call_tuple_and_enum
    // ANCHOR: call_move_right
    let mut point = Coordinates { x: 1, y: 1 };
    point.move_right(5);
    assert(point.x == 6);
    assert(point.y == 1);
    // ANCHOR_END: call_move_right
}\n```

Note that the variable num itself has to be declared as mutable for the above to compile.

Note It is not currently allowed to use mut without ref or vice versa for a function parameter.

Similarly, ref mut can be used with more complex data types such as:

```sway\nscript;

enum Color {
    Red: (),
    Blue: (),
}

// ANCHOR: increment
fn increment(ref mut num: u32) {
    let prev = num;
    num = prev + 1u32;
}
// ANCHOR_END: increment
// ANCHOR: tuple_and_enum
fn swap_tuple(ref mut pair: (u64, u64)) {
    let temp = pair.0;
    pair.0 = pair.1;
    pair.1 = temp;
}

fn update_color(ref mut color: Color, new_color: Color) {
    color = new_color;
}
// ANCHOR_END: tuple_and_enum
// ANCHOR: move_right
struct Coordinates {
    x: u64,
    y: u64,
}

impl Coordinates {
    fn move_right(ref mut self, distance: u64) {
        self.x += distance;
    }
}
// ANCHOR_END: move_right
fn main() {
    // ANCHOR: call_increment
    let mut num: u32 = 0;
    increment(num);
    assert(num == 1u32); // The function `increment()` modifies `num`
    // ANCHOR_END: call_increment
    // ANCHOR: call_tuple_and_enum
    let mut tuple = (42, 24);
    swap_tuple(tuple);
    assert(tuple.0 == 24); // The function `swap_tuple()` modifies `tuple.0`
    assert(tuple.1 == 42); // The function `swap_tuple()` modifies `tuple.1`
    let mut color = Color::Red;
    update_color(color, Color::Blue);
    assert(match color {
        Color::Blue => true,
        _ => false,
    }); // The function `update_color()` modifies the color to Blue
    // ANCHOR_END: call_tuple_and_enum
    // ANCHOR: call_move_right
    let mut point = Coordinates { x: 1, y: 1 };
    point.move_right(5);
    assert(point.x == 6);
    assert(point.y == 1);
    // ANCHOR_END: call_move_right
}\n```

We can then call these functions as shown below:

```sway\nscript;

enum Color {
    Red: (),
    Blue: (),
}

// ANCHOR: increment
fn increment(ref mut num: u32) {
    let prev = num;
    num = prev + 1u32;
}
// ANCHOR_END: increment
// ANCHOR: tuple_and_enum
fn swap_tuple(ref mut pair: (u64, u64)) {
    let temp = pair.0;
    pair.0 = pair.1;
    pair.1 = temp;
}

fn update_color(ref mut color: Color, new_color: Color) {
    color = new_color;
}
// ANCHOR_END: tuple_and_enum
// ANCHOR: move_right
struct Coordinates {
    x: u64,
    y: u64,
}

impl Coordinates {
    fn move_right(ref mut self, distance: u64) {
        self.x += distance;
    }
}
// ANCHOR_END: move_right
fn main() {
    // ANCHOR: call_increment
    let mut num: u32 = 0;
    increment(num);
    assert(num == 1u32); // The function `increment()` modifies `num`
    // ANCHOR_END: call_increment
    // ANCHOR: call_tuple_and_enum
    let mut tuple = (42, 24);
    swap_tuple(tuple);
    assert(tuple.0 == 24); // The function `swap_tuple()` modifies `tuple.0`
    assert(tuple.1 == 42); // The function `swap_tuple()` modifies `tuple.1`
    let mut color = Color::Red;
    update_color(color, Color::Blue);
    assert(match color {
        Color::Blue => true,
        _ => false,
    }); // The function `update_color()` modifies the color to Blue
    // ANCHOR_END: call_tuple_and_enum
    // ANCHOR: call_move_right
    let mut point = Coordinates { x: 1, y: 1 };
    point.move_right(5);
    assert(point.x == 6);
    assert(point.y == 1);
    // ANCHOR_END: call_move_right
}\n```

Note The only place, in a Sway program, where the ref keyword is valid is before a mutable function parameter.

Structs, Tuples, and Enums

Structs

Structs in Sway are a named grouping of types. You may also be familiar with structs via another name: product types. Sway does not make any significantly unique usages of structs; they are similar to most other languages which have structs. If you're coming from an object-oriented background, a struct is like the data attributes of an object.

Those data attributes are called fields and can be either public or private.

Private struct fields can be accessed only within the module in which their struct is declared. Public fields are accessible everywhere where the struct is accessible. This access control on the field level allows more fine grained encapsulation of data.

To explain these concepts, let's take a look at the following example, in which we have a module called data_structures.

In that module, we declare a struct named Foo with two fields. The first field is named bar, it is public and it accepts values of type u64. The second field is named baz, it is also public and it accepts bool values.

In a similar way, we define the structs Point, Line, and TupleInStruct. Since all those structs are public, and all their fields are public, they can be instantiated in other modules using the struct instantiation syntax as shown below.

On the other hand, the struct StructWithPrivateFields can be instantiated only within the data_structures module, because it contains private fields. To be able to create instances of such structs outside of the module in which they are declared, the struct must offer constructor associated functions.

```sway\n// the _data_structures_ module
library;

// Declare a struct type
pub struct Foo {
    pub bar: u64,
    pub baz: bool,
}

// Struct types for destructuring
pub struct Point {
    pub x: u64,
    pub y: u64,
}

pub struct Line {
    pub p1: Point,
    pub p2: Point,
}

pub struct TupleInStruct {
    pub nested_tuple: (u64, (u32, (bool, str))),
}

// Struct type instantiable only in the module _data_structures_
pub struct StructWithPrivateFields {
    pub public_field: u64,
    private_field: u64,
    other_private_field: u64,
}\n```

In order to instantiate the struct we use struct instantiation syntax, which is very similar to the declaration syntax except with expressions in place of types.

There are three ways to instantiate the struct.

  • Hard coding values for the fields
  • Passing in variables with names different than the struct fields
  • Using a shorthand notation via variables that are the same as the field names
```sway\nlibrary;

mod data_structures;
use data_structures::{Foo, Line, Point, TupleInStruct};

fn hardcoded_instantiation() -> Foo {
    // Instantiate `foo` as `Foo`
    let mut foo = Foo {
        bar: 42,
        baz: false,
    };

    // Access and write to "baz"
    foo.baz = true;

    // Return the struct
    foo
}

fn variable_instantiation() -> Foo {
    // Declare variables with the same names as the fields in `Foo`
    let number = 42;
    let truthness = false;

    // Instantiate `foo` as `Foo`
    let mut foo = Foo {
        bar: number,
        baz: truthness,
    };

    // Access and write to "baz"
    foo.baz = true;

    // Return the struct
    foo
}

fn shorthand_instantiation() -> Foo {
    // Declare variables with the same names as the fields in `Foo`
    let bar = 42;
    let baz = false;

    // Instantiate `foo` as `Foo`
    let mut foo = Foo { bar, baz };

    // Access and write to "baz"
    foo.baz = true;

    // Return the struct
    foo
}

fn struct_destructuring() {
    let point1 = Point { x: 0, y: 0 };
    // Destructure the values from the struct into variables
    let Point { x, y } = point1;

    let point2 = Point { x: 1, y: 1 };
    // If you do not care about specific struct fields then use ".." at the end of your variable list
    let Point { x, .. } = point2;

    let line = Line {
        p1: point1,
        p2: point2,
    };
    // Destructure the values from the nested structs into variables
    let Line {
        p1: Point { x: x0, y: y0 },
        p2: Point { x: x1, y: y1 },
    } = line;
    // You may also destructure tuples nested in structs and structs nested in tuples
    let tuple_in_struct = TupleInStruct {
        nested_tuple: (42u64, (42u32, (true, "ok"))),
    };
    let TupleInStruct {
        nested_tuple: (a, (b, (c, d))),
    } = tuple_in_struct;

    let struct_in_tuple = (Point { x: 2, y: 4 }, Point { x: 3, y: 6 });
    let (Point { x: x0, y: y0 }, Point { x: x1, y: y1 }) = struct_in_tuple;
}\n```

Note You can mix and match all 3 ways to instantiate the struct at the same time. Moreover, the order of the fields does not matter when instantiating however we encourage declaring the fields in alphabetical order and instantiating them in the same alphabetical order

Furthermore, multiple variables can be extracted from a struct using the destructuring syntax.

Struct Memory Layout

Note This information is not vital if you are new to the language, or programming in general

Structs have zero memory overhead. What that means is that in memory, each struct field is laid out sequentially. No metadata regarding the struct's name or other properties is preserved at runtime. In other words, structs are compile-time constructs. This is the same in Rust, but different in other languages with runtimes like Java.

Tuples

Tuples are a basic static-length type which contain multiple different types within themselves. The type of a tuple is defined by the types of the values within it, and a tuple can contain basic types as well as structs and enums.

You can access values directly by using the . syntax. Moreover, multiple variables can be extracted from a tuple using the destructuring syntax.

```sway\nlibrary;

fn tuple() {
    // You can declare the types yourself
    let tuple1: (u8, bool, u64) = (100, false, 10000);

    // Or have the types be inferred
    let mut tuple2 = (5, true, ("Sway", 8));

    // Retrieve values from tuples
    let number = tuple1.0;
    let sway = tuple2.2.1;

    // Destructure the values from the tuple into variables
    let (n1, truthness, n2) = tuple1;

    // If you do not care about specific values then use "_"
    let (_, truthness, _) = tuple2;

    // Internally mutate the tuple
    tuple2.1 = false;

    // Or change the values all at once (must keep the same data types)
    tuple2 = (9, false, ("Fuel", 99));
}\n```

Enums

Enumerations, or enums, are also known as sum types. An enum is a type that could be one of several variants. To declare an enum, you enumerate all potential variants.

Here, we have defined five potential colors. Each enum variant is just the color name. As there is no extra data associated with each variant, we say that each variant is of type (), or unit.

```sway\nlibrary;

// Declare the enum
enum Color {
    Blue: (),
    Green: (),
    Red: (),
    Silver: (),
    Grey: (),
}

fn main() {
    // To instantiate a variable with the value of an enum the syntax is
    let blue = Color::Blue;
    let silver = Color::Silver;
}\n```

Enums of Structs

It is also possible to have an enum variant contain extra data. Take a look at this more substantial example, which combines struct declarations with enum variants:

```sway\nlibrary;

struct Item {
    price: u64,
    amount: u64,
    id: u64,
}

enum MyEnum {
    Item: Item,
}

fn main() {
    let my_enum = MyEnum::Item(Item {
        price: 5,
        amount: 2,
        id: 42,
    });
}\n```

Enums of Enums

It is possible to define enums of enums:

```sway\nlibrary;

pub enum Error {
    StateError: StateError,
    UserError: UserError,
}

pub enum StateError {
    Void: (),
    Pending: (),
    Completed: (),
}

pub enum UserError {
    InsufficientPermissions: (),
    Unauthorized: (),
}\n```

Preferred usage

The preferred way to use enums is to use the individual (not nested) enums directly because they are easy to follow and the lines are short:

```sway\nlibrary;

use ::enum_of_enums::{StateError, UserError};

fn preferred() {
    let error1 = StateError::Void;
    let error2 = UserError::Unauthorized;
}\n```

Inadvisable

If you wish to use the nested form of enums via the Error enum from the example above, then you can instantiate them into variables using the following syntax:

```sway\nlibrary;

use ::enum_of_enums::{Error, StateError, UserError};

fn avoid() {
    let error1 = Error::StateError(StateError::Void);
    let error2 = Error::UserError(UserError::Unauthorized);
}\n```

Key points to note:

  • You must import all of the enums you need instead of just the Error enum
  • The lines may get unnecessarily long (depending on the names)
  • The syntax is not the most ergonomic

Enum Memory Layout

Note This information is not vital if you are new to the language, or programming in general.

Enums do have some memory overhead. To know which variant is being represented, Sway stores a one-word (8-byte) tag for the enum variant. The space reserved after the tag is equivalent to the size of the largest enum variant. So, to calculate the size of an enum in memory, add 8 bytes to the size of the largest variant. For example, in the case of Color above, where the variants are all (), the size would be 8 bytes since the size of the largest variant is 0 bytes.

Methods and Associated Functions

Methods

Methods are similar to functions in that we declare them with the fn keyword and they have parameters and return a value. However, unlike functions, Methods are defined within the context of a struct (or enum), and either refers to that type or mutates it. The first parameter of a method is always self, which represents the instance of the struct (or enum) the method is being called on.

Associated Functions

Associated functions are very similar to methods, in that they are also defined in the context of a struct or enum, but they do not actually use any of the data in the struct and as a result do not take self as a parameter. Associated functions could be standalone functions, but they are included in a specific type for organizational or semantic reasons.

Constructors

Constructors are associated functions that construct, or in other words instantiate, new instances of a type. Their return type is always the type itself. E.g., public structs that have private fields must provide a public constructor, or otherwise they cannot be instantiated outside of the module in which they are declared.

Declaring Methods and Associated Functions

To declare methods and associated functions for a struct or enum, use an impl block. Here, impl is short for implementation.

```sway\nscript;

struct Foo {
    bar: u64,
    baz: bool,
}

impl Foo {
    // this is a _method_, as it takes `self` as a parameter.
    fn is_baz_true(self) -> bool {
        self.baz
    }

    // this is an _associated function_, since it does not take `self` as a parameter.
    // it is at the same time a _constructor_ because it instantiates and returns
    // a new instance of `Foo`.
    fn new_foo(number: u64, boolean: bool) -> Foo {
        Foo {
            bar: number,
            baz: boolean,
        }
    }
}

fn main() {
    let foo = Foo::new_foo(42, true);
    assert(foo.is_baz_true());
}\n```

To call a method, simply use dot syntax: foo.iz_baz_true().

Similarly to free functions, methods and associated functions may accept ref mut parameters.

For example:

```sway\nscript;

enum Color {
    Red: (),
    Blue: (),
}

// ANCHOR: increment
fn increment(ref mut num: u32) {
    let prev = num;
    num = prev + 1u32;
}
// ANCHOR_END: increment
// ANCHOR: tuple_and_enum
fn swap_tuple(ref mut pair: (u64, u64)) {
    let temp = pair.0;
    pair.0 = pair.1;
    pair.1 = temp;
}

fn update_color(ref mut color: Color, new_color: Color) {
    color = new_color;
}
// ANCHOR_END: tuple_and_enum
// ANCHOR: move_right
struct Coordinates {
    x: u64,
    y: u64,
}

impl Coordinates {
    fn move_right(ref mut self, distance: u64) {
        self.x += distance;
    }
}
// ANCHOR_END: move_right
fn main() {
    // ANCHOR: call_increment
    let mut num: u32 = 0;
    increment(num);
    assert(num == 1u32); // The function `increment()` modifies `num`
    // ANCHOR_END: call_increment
    // ANCHOR: call_tuple_and_enum
    let mut tuple = (42, 24);
    swap_tuple(tuple);
    assert(tuple.0 == 24); // The function `swap_tuple()` modifies `tuple.0`
    assert(tuple.1 == 42); // The function `swap_tuple()` modifies `tuple.1`
    let mut color = Color::Red;
    update_color(color, Color::Blue);
    assert(match color {
        Color::Blue => true,
        _ => false,
    }); // The function `update_color()` modifies the color to Blue
    // ANCHOR_END: call_tuple_and_enum
    // ANCHOR: call_move_right
    let mut point = Coordinates { x: 1, y: 1 };
    point.move_right(5);
    assert(point.x == 6);
    assert(point.y == 1);
    // ANCHOR_END: call_move_right
}\n```

and when called:

```sway\nscript;

enum Color {
    Red: (),
    Blue: (),
}

// ANCHOR: increment
fn increment(ref mut num: u32) {
    let prev = num;
    num = prev + 1u32;
}
// ANCHOR_END: increment
// ANCHOR: tuple_and_enum
fn swap_tuple(ref mut pair: (u64, u64)) {
    let temp = pair.0;
    pair.0 = pair.1;
    pair.1 = temp;
}

fn update_color(ref mut color: Color, new_color: Color) {
    color = new_color;
}
// ANCHOR_END: tuple_and_enum
// ANCHOR: move_right
struct Coordinates {
    x: u64,
    y: u64,
}

impl Coordinates {
    fn move_right(ref mut self, distance: u64) {
        self.x += distance;
    }
}
// ANCHOR_END: move_right
fn main() {
    // ANCHOR: call_increment
    let mut num: u32 = 0;
    increment(num);
    assert(num == 1u32); // The function `increment()` modifies `num`
    // ANCHOR_END: call_increment
    // ANCHOR: call_tuple_and_enum
    let mut tuple = (42, 24);
    swap_tuple(tuple);
    assert(tuple.0 == 24); // The function `swap_tuple()` modifies `tuple.0`
    assert(tuple.1 == 42); // The function `swap_tuple()` modifies `tuple.1`
    let mut color = Color::Red;
    update_color(color, Color::Blue);
    assert(match color {
        Color::Blue => true,
        _ => false,
    }); // The function `update_color()` modifies the color to Blue
    // ANCHOR_END: call_tuple_and_enum
    // ANCHOR: call_move_right
    let mut point = Coordinates { x: 1, y: 1 };
    point.move_right(5);
    assert(point.x == 6);
    assert(point.y == 1);
    // ANCHOR_END: call_move_right
}\n```

Constants

Constants are similar to variables; however, there are a few differences:

  • Constants are always evaluated at compile-time.
  • Constants can be declared both inside of a function and at global / impl scope.
  • The mut keyword cannot be used with constants.
const ID: u32 = 0;

Constant initializer expressions can be quite complex, but they cannot use, for instance, assembly instructions, storage access, mutable variables, loops and return statements. Although, function calls, primitive types and compound data structures are perfectly fine to use:

fn bool_to_num(b: bool) -> u64 {
    if b {
        1
    } else {
        0
    }
}

fn arr_wrapper(a: u64, b: u64, c: u64) -> [u64; 3] {
    [a, b, c]
}

const ARR2 = arr_wrapper(bool_to_num(1) + 42, 2, 3);

Associated Constants

Associated constants are constants associated with a type and can be declared in an impl block or in a trait definition.

Associated constants declared inside a trait definition may omit their initializers to indicate that each implementation of the trait must specify those initializers.

The identifier is the name of the constant used in the path. The type is the type that the definition has to implement.

You can define an associated const directly in the interface surface of a trait:

script;

trait ConstantId {
    const ID: u32 = 0;
}

Alternatively, you can also declare it in the trait, and implement it in the interface of the types implementing the trait.

script;

trait ConstantId {
    const ID: u32;
}

struct Struct {}

impl ConstantId for Struct {
    const ID: u32 = 1;
}

fn main() -> u32 {
    Struct::ID
}

impl self Constants

Constants can also be declared inside non-trait impl blocks.

script;

struct Point {
    x: u64,
    y: u64,
}

impl Point {
    const ZERO: Point = Point { x: 0, y: 0 };
}

fn main() -> u64  {
    Point::ZERO.x
}

Configurable Constants

Configurable constants are special constants that behave like regular constants in the sense that they cannot change during program execution, but they can be configured after the Sway program has been built. The Rust and TS SDKs allow updating the values of these constants by injecting new values for them directly in the bytecode without having to build the program again. These are useful for contract factories and behave somewhat similarly to immutable variables from languages like Solidity.

Configurable constants are declared inside a configurable block and require a type ascription and an initializer as follows:

```sway\ncontract;

enum EnumWithGeneric<D> {
    VariantOne: D,
    VariantTwo: (),
}

struct StructWithGeneric<D> {
    field_1: D,
    field_2: u64,
}

// ANCHOR: configurable_block
configurable {
    U8: u8 = 8u8,
    BOOL: bool = true,
    ARRAY: [u32; 3] = [253u32, 254u32, 255u32],
    STR_4: str[4] = __to_str_array("fuel"),
    STRUCT: StructWithGeneric<u8> = StructWithGeneric {
        field_1: 8u8,
        field_2: 16,
    },
    ENUM: EnumWithGeneric<bool> = EnumWithGeneric::VariantOne(true),
}
// ANCHOR_END: configurable_block 

abi TestContract {
    fn return_configurables() -> (u8, bool, [u32; 3], str[4], StructWithGeneric<u8>);
}

impl TestContract for Contract {
    // ANCHOR: using_configurables
    fn return_configurables() -> (u8, bool, [u32; 3], str[4], StructWithGeneric<u8>) {
        (U8, BOOL, ARRAY, STR_4, STRUCT)
    }
    // ANCHOR_END: using_configurables
}\n```

At most one configurable block is allowed in a Sway project. Moreover, configurable blocks are not allowed in libraries.

Configurable constants can be read directly just like regular constants:

```sway\ncontract;

enum EnumWithGeneric<D> {
    VariantOne: D,
    VariantTwo: (),
}

struct StructWithGeneric<D> {
    field_1: D,
    field_2: u64,
}

// ANCHOR: configurable_block
configurable {
    U8: u8 = 8u8,
    BOOL: bool = true,
    ARRAY: [u32; 3] = [253u32, 254u32, 255u32],
    STR_4: str[4] = __to_str_array("fuel"),
    STRUCT: StructWithGeneric<u8> = StructWithGeneric {
        field_1: 8u8,
        field_2: 16,
    },
    ENUM: EnumWithGeneric<bool> = EnumWithGeneric::VariantOne(true),
}
// ANCHOR_END: configurable_block 

abi TestContract {
    fn return_configurables() -> (u8, bool, [u32; 3], str[4], StructWithGeneric<u8>);
}

impl TestContract for Contract {
    // ANCHOR: using_configurables
    fn return_configurables() -> (u8, bool, [u32; 3], str[4], StructWithGeneric<u8>) {
        (U8, BOOL, ARRAY, STR_4, STRUCT)
    }
    // ANCHOR_END: using_configurables
}\n```

Comments and Logging

Comments

Comments in Sway start with two slashes and continue until the end of the line. For comments that extend beyond a single line, you'll need to include // on each line.

// hello world
// let's make a couple of lines
// commented.

You can also place comments at the ends of lines containing code.

fn main() {
    let baz = 8; // Eight is a lucky number
}

You can also do block comments

fn main() {
    /*
    You can write on multiple lines
    like this if you want
    */
    let baz = 8;
}

Logging

The logging library provides a generic log function that can be imported using use std::logging::log and used to log variables of any type. Each call to log appends a receipt to the list of receipts. There are two types of receipts that a log can generate: Log and LogData.

fn log_values(){
  // Generates a Log receipt
  log(42);

  // Generates a LogData receipt
  let string = "sway";
  log(string);
}

Log Receipt

The Log receipt is generated for non-reference types, namely bool, u8, u16, u32, and u64.

For example, logging an integer variable x that holds the value 42 using log(x) may generate the following receipt:

"Log": {
  "id": "0000000000000000000000000000000000000000000000000000000000000000",
  "is": 10352,
  "pc": 10404,
  "ra": 42,
  "rb": 1018205,
  "rc": 0,
  "rd": 0
}

Note that ra will include the value being logged. The additional registers rc and rd will be zero when using log while rb may include a non-zero value representing a unique ID for the log instance. The unique ID is not meaningful on its own but allows the Rust and the TS SDKs to know the type of the data being logged, by looking up the log ID in the JSON ABI file.

LogData Receipt

LogData is generated for reference types which include all types except for non_reference types; and for non-reference types bigger than 64-bit integers, for example, u256;

For example, logging a b256 variable b that holds the value 0x1111111111111111111111111111111111111111111111111111111111111111 using log(b) may generate the following receipt:

"LogData": {
  "data": "1111111111111111111111111111111111111111111111111111111111111111",
  "digest": "02d449a31fbb267c8f352e9968a79e3e5fc95c1bbeaa502fd6454ebde5a4bedc",
  "id": "0000000000000000000000000000000000000000000000000000000000000000",
  "is": 10352,
  "len": 32,
  "pc": 10444,
  "ptr": 10468,
  "ra": 0,
  "rb": 1018194
}

Note that data in the receipt above will include the value being logged as a hexadecimal. Similarly to the Log receipt, additional registers are written: ra will always be zero when using log, while rb will contain a unique ID for the log instance.

Note The Rust SDK exposes APIs that allow you to retrieve the logged values and display them nicely based on their types as indicated in the JSON ABI file.

Control Flow

if expressions

Sway supports if, else, and else if expressions that allow you to branch your code depending on conditions.

For example:

fn main() {
    let number = 6;

    if number % 4 == 0 {
        // do something
    } else if number % 3 == 0 {
        // do something else
    } else {
        // do something else
    }
}

Using if in a let statement

Like Rust, ifs are expressions in Sway. What this means is you can use if expressions on the right side of a let statement to assign the outcome to a variable.

let my_data = if some_bool < 10 { foo() } else { bar() };

Note that all branches of the if expression must return a value of the same type.

match expressions

Sway supports advanced pattern matching through exhaustive match expressions. Unlike an if expression, a match expression asserts at compile time that all possible patterns have been matched. If you don't handle all the patterns, you will get compiler error indicating that your match expression is non-exhaustive.

The basic syntax of a match expression is as follows:

let result = match expression {
    pattern1 => code_to_execute_if_expression_matches_pattern1,
    pattern2 => code_to_execute_if_expression_matches_pattern2,
    pattern3 | pattern4 => code_to_execute_if_expression_matches_pattern3_or_pattern4
    ...
    _ => code_to_execute_if_expression_matches_no_pattern,
}

Some examples of how you can use a match expression:

```sway\nscript;

// helper functions for our example
fn on_even(num: u64) {
    // do something with even numbers
}
fn on_odd(num: u64) {
    // do something with odd numbers
}

fn main(num: u64) -> u64 {
    // Match as an expression
    let is_even = match num % 2 {
        0 => true,
        _ => false,
    };

    // Match as control flow
    let x = 12;
    match x {
        5 => on_odd(x),
        _ => on_even(x),
    };

    // Match an enum
    enum Weather {
        Sunny: (),
        Rainy: (),
        Cloudy: (),
        Snowy: (),
    }
    let current_weather = Weather::Sunny;
    let avg_temp = match current_weather {
        Weather::Sunny => 80,
        Weather::Rainy => 50,
        Weather::Cloudy => 60,
        Weather::Snowy => 20,
    };

    let is_sunny = match current_weather {
        Weather::Sunny => true,
        Weather::Rainy | Weather::Cloudy | Weather::Snowy => false,
    };

    // match expression used for a return
    let outside_temp = Weather::Sunny;
    match outside_temp {
        Weather::Sunny => 80,
        Weather::Rainy => 50,
        Weather::Cloudy => 60,
        Weather::Snowy => 20,
    }
}\n```

Loops

while

This is what a while loop looks like:

while counter < 10 {
    counter = counter + 1;
}

You need the while keyword, some condition (value < 10 in this case) which will be evaluated each iteration, and a block of code inside the curly braces ({...}) to execute each iteration.

for

This is what a for loop that computes the sum of a vector of numbers looks like:

for element in vector.iter() {
    sum += element;
}

You need the for keyword, some pattern that contains variable names such as element in this case, the ìn keyword followed by an iterator, and a block of code inside the curly braces ({...}) to execute each iteration. vector.iter() in the example above returns an iterator for the vector. In each iteration, the value of element is updated with the next value in the iterator until the end of the vector is reached and the for loop iteration ends.

Modifying the vector during iteration, by e.g. adding or removing elements, is a logical error and results in an undefined behavior:

// The behavior of this `for` loop is undefined because
// the `vector` gets modified within the loop.
for element in vector.iter() {
    if element == 3 {
        vector.push(6); // Modification of the vector!
    }
}

break and continue

break and continue keywords are available to use inside the body of a while or for loop. The purpose of the break statement is to break out of a loop early:

```sway\nscript;

// ANCHOR: break_example
fn break_example() -> u64 {
    let mut counter = 1;
    let mut sum = 0;
    let num = 10;
    while true {
        if counter > num {
            break;
        }
        sum += counter;
        counter += 1;
    }
    sum // 1 + 2 + .. + 10 = 55
}
// ANCHOR_END: break_example
// ANCHOR: continue_example
fn continue_example() -> u64 {
    let mut counter = 0;
    let mut sum = 0;
    let num = 10;
    while counter < num {
        counter += 1;
        if counter % 2 == 0 {
            continue;
        }
        sum += counter;
    }
    sum // 1 + 3 + .. + 9 = 25
}
// ANCHOR_END: continue_example
fn main() -> u64 {
    break_example() + continue_example() // 55 + 25 = 80
}\n```

The purpose of the continue statement is to skip a portion of a loop in an iteration and jump directly into the next iteration:

```sway\nscript;

// ANCHOR: break_example
fn break_example() -> u64 {
    let mut counter = 1;
    let mut sum = 0;
    let num = 10;
    while true {
        if counter > num {
            break;
        }
        sum += counter;
        counter += 1;
    }
    sum // 1 + 2 + .. + 10 = 55
}
// ANCHOR_END: break_example
// ANCHOR: continue_example
fn continue_example() -> u64 {
    let mut counter = 0;
    let mut sum = 0;
    let num = 10;
    while counter < num {
        counter += 1;
        if counter % 2 == 0 {
            continue;
        }
        sum += counter;
    }
    sum // 1 + 3 + .. + 9 = 25
}
// ANCHOR_END: continue_example
fn main() -> u64 {
    break_example() + continue_example() // 55 + 25 = 80
}\n```

Nested loops

You can also use nested while loops if needed:

while condition_1 == true {
    // do stuff...
    while condition_2 == true {
        // do more stuff...
    }
}

Blockchain Development with Sway

Sway is fundamentally a blockchain language. Because of this, it has some features and requirements that you may not have seen in general-purpose programming languages.

These are also some concepts related to the FuelVM and Fuel ecosystem that you may utilize when writing Sway.

Hashing and Cryptography

The Sway standard library provides easy access to a selection of cryptographic hash functions (sha256 and EVM-compatible keccak256), and EVM-compatible secp256k1-based signature recovery operations.

Hashing

```sway\nscript;

use std::hash::*;

impl Hash for Location {
    fn hash(self, ref mut state: Hasher) {
        match self {
            Location::Earth => {
                0_u8.hash(state);
            }
            Location::Mars => {
                1_u8.hash(state);
            }
        }
    }
}

impl Hash for Stats {
    fn hash(self, ref mut state: Hasher) {
        self.strength.hash(state);
        self.agility.hash(state);
    }
}

impl Hash for Person {
    fn hash(self, ref mut state: Hasher) {
        self.name.hash(state);
        self.age.hash(state);
        self.alive.hash(state);
        self.location.hash(state);
        self.stats.hash(state);
        self.some_tuple.hash(state);
        self.some_array.hash(state);
        self.some_b256.hash(state);
    }
}

const VALUE_A = 0x9280359a3b96819889d30614068715d634ad0cf9bba70c0f430a8c201138f79f;

enum Location {
    Earth: (),
    Mars: (),
}

struct Person {
    name: str,
    age: u64,
    alive: bool,
    location: Location,
    stats: Stats,
    some_tuple: (bool, u64),
    some_array: [u64; 2],
    some_b256: b256,
}

struct Stats {
    strength: u64,
    agility: u64,
}

fn main() {
    let zero = b256::min();
    // Use the generic sha256 to hash some integers
    let sha_hashed_u8 = sha256(u8::max());
    let sha_hashed_u16 = sha256(u16::max());
    let sha_hashed_u32 = sha256(u32::max());
    let sha_hashed_u64 = sha256(u64::max());

    // Or hash a b256
    let sha_hashed_b256 = sha256(VALUE_A);

    // You can hash booleans too
    let sha_hashed_bool = sha256(true);

    // Strings are not a problem either
    let sha_hashed_str = sha256("Fastest Modular Execution Layer!");

    // Tuples of any size work too
    let sha_hashed_tuple = sha256((true, 7));

    // As do arrays
    let sha_hashed_array = sha256([4, 5, 6]);

    // Enums work too
    let sha_hashed_enum = sha256(Location::Earth);

    // Complex structs are not a problem
    let sha_hashed_struct = sha256(Person {
        name: "John",
        age: 9000,
        alive: true,
        location: Location::Mars,
        stats: Stats {
            strength: 10,
            agility: 9,
        },
        some_tuple: (true, 8),
        some_array: [17, 76],
        some_b256: zero,
    });

    log(sha_hashed_u8);
    log(sha_hashed_u16);
    log(sha_hashed_u32);
    log(sha_hashed_u64);
    log(sha_hashed_b256);
    log(sha_hashed_bool);
    log(sha_hashed_str);
    log(sha_hashed_tuple);
    log(sha_hashed_array);
    log(sha_hashed_enum);
    log(sha_hashed_struct);

    // Use the generic keccak256 to hash some integers
    let keccak_hashed_u8 = keccak256(u8::max());
    let keccak_hashed_u16 = keccak256(u16::max());
    let keccak_hashed_u32 = keccak256(u32::max());
    let keccak_hashed_u64 = keccak256(u64::max());

    // Or hash a b256
    let keccak_hashed_b256 = keccak256(VALUE_A);

    // You can hash booleans too
    let keccak_hashed_bool = keccak256(true);

    // Strings are not a problem either
    let keccak_hashed_str = keccak256("Fastest Modular Execution Layer!");

    // Tuples of any size work too
    let keccak_hashed_tuple = keccak256((true, 7));

    // As do arrays
    let keccak_hashed_array = keccak256([4, 5, 6]);

    // Enums work too
    let keccak_hashed_enum = keccak256(Location::Earth);

    // Complex structs are not a problem
    let keccak_hashed_struct = keccak256(Person {
        name: "John",
        age: 9000,
        alive: true,
        location: Location::Mars,
        stats: Stats {
            strength: 10,
            agility: 9,
        },
        some_tuple: (true, 8),
        some_array: [17, 76],
        some_b256: zero,
    });

    log(keccak_hashed_u8);
    log(keccak_hashed_u16);
    log(keccak_hashed_u32);
    log(keccak_hashed_u64);
    log(keccak_hashed_b256);
    log(keccak_hashed_bool);
    log(keccak_hashed_str);
    log(keccak_hashed_tuple);
    log(keccak_hashed_array);
    log(keccak_hashed_enum);
    log(keccak_hashed_struct);
}\n```

Cryptographic Signature Recovery and Verification

Fuel supports 3 asymmetric cryptographic signature schemes; Secp256k1, Secp256r1, and Ed25519.

Public Key Recovery

Given a Signature and a sign Message, you can recover a PublicKey.

```sway\nscript;

fn main() {}

use std::{
    crypto::{
        ed25519::*,
        message::*,
        public_key::*,
        secp256k1::*,
        secp256r1::*,
        signature::*,
    },
    hash::{
        Hash,
        sha256,
    },
    vm::evm::evm_address::EvmAddress,
};

fn public_key_recovery() {
    // ANCHOR: public_key_recovery
    // Secp256rk1 Public Key Recovery
    let secp256k1_signature: Signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered public key pair.
    let secp256k1_public_key = secp256k1_signature.recover(signed_message);
    assert(secp256k1_public_key.is_ok());
    assert(
        secp256k1_public_key
            .unwrap() == PublicKey::from((
            0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
            0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
        )),
    );

    // Secp256r1 Public Key Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A recovered public key pair.
    let secp256r1_public_key = secp256r1_signature.recover(signed_message);
    assert(secp256r1_public_key.is_ok());
    assert(
        secp256r1_public_key
            .unwrap() == PublicKey::from((
            0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
            0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
        )),
    );
    // ANCHOR_END: public_key_recovery
}

fn address_recovery() {
    // ANCHOR: address_recovery
    // Secp256k1 Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered Fuel address.
    let secp256k1_address = secp256k1_signature.address(signed_message);
    assert(secp256k1_address.is_ok());
    assert(
        secp256k1_address
            .unwrap() == Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881),
    );

    // Secp256r1 Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered Fuel address.
    let secp256r1_address = secp256r1_signature.address(signed_message);
    assert(secp256r1_address.is_ok());
    assert(
        secp256r1_address
            .unwrap() == Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a),
    );

    // ANCHOR_END: address_recovery
}

fn evm_address_recovery() {
    // ANCHOR: evm_address_recovery
    // Secp256k1 EVM Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_evm_address = secp256k1_signature.evm_address(signed_message);
    assert(secp256k1_evm_address.is_ok());
    assert(
        secp256k1_evm_address
            .unwrap() == EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb),
    );

    // Secp256r1 EVM Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_evm_address = secp256r1_signature.evm_address(signed_message);
    assert(secp256r1_evm_address.is_ok());
    assert(
        secp256r1_evm_address
            .unwrap() == EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a),
    );
    // ANCHOR_END: evm_address_recovery
}

fn signature_verification() {
    // ANCHOR: signature_verification
    // Secp256k1 Signature Verification
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let secp256k1_public_key = PublicKey::from((
        0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
        0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified public key
    let secp256k1_verified = secp256k1_signature.verify(secp256k1_public_key, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Signature Verification
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let secp256r1_public_key = PublicKey::from((
        0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
        0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
    ));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A verified public key 
    let secp256r1_verified = secp256r1_signature.verify(secp256r1_public_key, signed_message);
    assert(secp256r1_verified.is_ok());

    // Ed25519 Signature Verification
    let ed25519_public_key = PublicKey::from(0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10);
    let ed25519_signature = Signature::Ed25519(Ed25519::from((
        0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545,
        0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00,
    )));
    let hashed_message = Message::from(sha256(b256::zero()));
    // A verified public key  
    let ed25519_verified = ed25519_signature.verify(ed25519_public_key, hashed_message);
    assert(ed25519_verified.is_ok());
    // ANCHOR_END: signature_verification
}

fn address_verification() {
    // ANCHOR: address_verification
    // Secp256k1 Address Verification
    let secp256k1_address = Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881);
    let secp256k1_signature = Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified address
    let secp256k1_verified = secp256k1_signature.verify_address(secp256k1_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A verified address
    let secp256r1_verified = secp256r1_signature.verify_address(secp256r1_address, signed_message);
    assert(secp256r1_verified.is_ok());

    // ANCHOR_END: address_verification
}

fn evm_address_verification() {
    // ANCHOR: evm_address_verification
    // Secp256k1 Address Verification
    let secp256k1_evm_address = EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb);
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_verified = secp256k1_signature.verify_evm_address(secp256k1_evm_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_evm_address = EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_verified = secp256r1_signature.verify_evm_address(secp256r1_evm_address, signed_message);
    assert(secp256r1_verified.is_ok());
    // ANCHOR_END: evm_address_verification
}\n```

Signed Message Address Recovery

Given a Signature and signed Message, you can recover a Fuel Address.

```sway\nscript;

fn main() {}

use std::{
    crypto::{
        ed25519::*,
        message::*,
        public_key::*,
        secp256k1::*,
        secp256r1::*,
        signature::*,
    },
    hash::{
        Hash,
        sha256,
    },
    vm::evm::evm_address::EvmAddress,
};

fn public_key_recovery() {
    // ANCHOR: public_key_recovery
    // Secp256rk1 Public Key Recovery
    let secp256k1_signature: Signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered public key pair.
    let secp256k1_public_key = secp256k1_signature.recover(signed_message);
    assert(secp256k1_public_key.is_ok());
    assert(
        secp256k1_public_key
            .unwrap() == PublicKey::from((
            0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
            0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
        )),
    );

    // Secp256r1 Public Key Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A recovered public key pair.
    let secp256r1_public_key = secp256r1_signature.recover(signed_message);
    assert(secp256r1_public_key.is_ok());
    assert(
        secp256r1_public_key
            .unwrap() == PublicKey::from((
            0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
            0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
        )),
    );
    // ANCHOR_END: public_key_recovery
}

fn address_recovery() {
    // ANCHOR: address_recovery
    // Secp256k1 Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered Fuel address.
    let secp256k1_address = secp256k1_signature.address(signed_message);
    assert(secp256k1_address.is_ok());
    assert(
        secp256k1_address
            .unwrap() == Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881),
    );

    // Secp256r1 Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered Fuel address.
    let secp256r1_address = secp256r1_signature.address(signed_message);
    assert(secp256r1_address.is_ok());
    assert(
        secp256r1_address
            .unwrap() == Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a),
    );

    // ANCHOR_END: address_recovery
}

fn evm_address_recovery() {
    // ANCHOR: evm_address_recovery
    // Secp256k1 EVM Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_evm_address = secp256k1_signature.evm_address(signed_message);
    assert(secp256k1_evm_address.is_ok());
    assert(
        secp256k1_evm_address
            .unwrap() == EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb),
    );

    // Secp256r1 EVM Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_evm_address = secp256r1_signature.evm_address(signed_message);
    assert(secp256r1_evm_address.is_ok());
    assert(
        secp256r1_evm_address
            .unwrap() == EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a),
    );
    // ANCHOR_END: evm_address_recovery
}

fn signature_verification() {
    // ANCHOR: signature_verification
    // Secp256k1 Signature Verification
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let secp256k1_public_key = PublicKey::from((
        0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
        0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified public key
    let secp256k1_verified = secp256k1_signature.verify(secp256k1_public_key, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Signature Verification
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let secp256r1_public_key = PublicKey::from((
        0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
        0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
    ));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A verified public key 
    let secp256r1_verified = secp256r1_signature.verify(secp256r1_public_key, signed_message);
    assert(secp256r1_verified.is_ok());

    // Ed25519 Signature Verification
    let ed25519_public_key = PublicKey::from(0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10);
    let ed25519_signature = Signature::Ed25519(Ed25519::from((
        0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545,
        0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00,
    )));
    let hashed_message = Message::from(sha256(b256::zero()));
    // A verified public key  
    let ed25519_verified = ed25519_signature.verify(ed25519_public_key, hashed_message);
    assert(ed25519_verified.is_ok());
    // ANCHOR_END: signature_verification
}

fn address_verification() {
    // ANCHOR: address_verification
    // Secp256k1 Address Verification
    let secp256k1_address = Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881);
    let secp256k1_signature = Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified address
    let secp256k1_verified = secp256k1_signature.verify_address(secp256k1_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A verified address
    let secp256r1_verified = secp256r1_signature.verify_address(secp256r1_address, signed_message);
    assert(secp256r1_verified.is_ok());

    // ANCHOR_END: address_verification
}

fn evm_address_verification() {
    // ANCHOR: evm_address_verification
    // Secp256k1 Address Verification
    let secp256k1_evm_address = EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb);
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_verified = secp256k1_signature.verify_evm_address(secp256k1_evm_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_evm_address = EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_verified = secp256r1_signature.verify_evm_address(secp256r1_evm_address, signed_message);
    assert(secp256r1_verified.is_ok());
    // ANCHOR_END: evm_address_verification
}\n```

Signed Message EVM Address Recovery

Recovery of EVM addresses is also supported.

```sway\nscript;

fn main() {}

use std::{
    crypto::{
        ed25519::*,
        message::*,
        public_key::*,
        secp256k1::*,
        secp256r1::*,
        signature::*,
    },
    hash::{
        Hash,
        sha256,
    },
    vm::evm::evm_address::EvmAddress,
};

fn public_key_recovery() {
    // ANCHOR: public_key_recovery
    // Secp256rk1 Public Key Recovery
    let secp256k1_signature: Signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered public key pair.
    let secp256k1_public_key = secp256k1_signature.recover(signed_message);
    assert(secp256k1_public_key.is_ok());
    assert(
        secp256k1_public_key
            .unwrap() == PublicKey::from((
            0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
            0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
        )),
    );

    // Secp256r1 Public Key Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A recovered public key pair.
    let secp256r1_public_key = secp256r1_signature.recover(signed_message);
    assert(secp256r1_public_key.is_ok());
    assert(
        secp256r1_public_key
            .unwrap() == PublicKey::from((
            0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
            0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
        )),
    );
    // ANCHOR_END: public_key_recovery
}

fn address_recovery() {
    // ANCHOR: address_recovery
    // Secp256k1 Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered Fuel address.
    let secp256k1_address = secp256k1_signature.address(signed_message);
    assert(secp256k1_address.is_ok());
    assert(
        secp256k1_address
            .unwrap() == Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881),
    );

    // Secp256r1 Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered Fuel address.
    let secp256r1_address = secp256r1_signature.address(signed_message);
    assert(secp256r1_address.is_ok());
    assert(
        secp256r1_address
            .unwrap() == Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a),
    );

    // ANCHOR_END: address_recovery
}

fn evm_address_recovery() {
    // ANCHOR: evm_address_recovery
    // Secp256k1 EVM Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_evm_address = secp256k1_signature.evm_address(signed_message);
    assert(secp256k1_evm_address.is_ok());
    assert(
        secp256k1_evm_address
            .unwrap() == EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb),
    );

    // Secp256r1 EVM Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_evm_address = secp256r1_signature.evm_address(signed_message);
    assert(secp256r1_evm_address.is_ok());
    assert(
        secp256r1_evm_address
            .unwrap() == EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a),
    );
    // ANCHOR_END: evm_address_recovery
}

fn signature_verification() {
    // ANCHOR: signature_verification
    // Secp256k1 Signature Verification
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let secp256k1_public_key = PublicKey::from((
        0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
        0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified public key
    let secp256k1_verified = secp256k1_signature.verify(secp256k1_public_key, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Signature Verification
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let secp256r1_public_key = PublicKey::from((
        0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
        0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
    ));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A verified public key 
    let secp256r1_verified = secp256r1_signature.verify(secp256r1_public_key, signed_message);
    assert(secp256r1_verified.is_ok());

    // Ed25519 Signature Verification
    let ed25519_public_key = PublicKey::from(0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10);
    let ed25519_signature = Signature::Ed25519(Ed25519::from((
        0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545,
        0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00,
    )));
    let hashed_message = Message::from(sha256(b256::zero()));
    // A verified public key  
    let ed25519_verified = ed25519_signature.verify(ed25519_public_key, hashed_message);
    assert(ed25519_verified.is_ok());
    // ANCHOR_END: signature_verification
}

fn address_verification() {
    // ANCHOR: address_verification
    // Secp256k1 Address Verification
    let secp256k1_address = Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881);
    let secp256k1_signature = Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified address
    let secp256k1_verified = secp256k1_signature.verify_address(secp256k1_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A verified address
    let secp256r1_verified = secp256r1_signature.verify_address(secp256r1_address, signed_message);
    assert(secp256r1_verified.is_ok());

    // ANCHOR_END: address_verification
}

fn evm_address_verification() {
    // ANCHOR: evm_address_verification
    // Secp256k1 Address Verification
    let secp256k1_evm_address = EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb);
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_verified = secp256k1_signature.verify_evm_address(secp256k1_evm_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_evm_address = EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_verified = secp256r1_signature.verify_evm_address(secp256r1_evm_address, signed_message);
    assert(secp256r1_verified.is_ok());
    // ANCHOR_END: evm_address_verification
}\n```

Public Key Signature Verification

Given a Signature, PublicKey, and Message, you can verify that the message was signed using the public key.

```sway\nscript;

fn main() {}

use std::{
    crypto::{
        ed25519::*,
        message::*,
        public_key::*,
        secp256k1::*,
        secp256r1::*,
        signature::*,
    },
    hash::{
        Hash,
        sha256,
    },
    vm::evm::evm_address::EvmAddress,
};

fn public_key_recovery() {
    // ANCHOR: public_key_recovery
    // Secp256rk1 Public Key Recovery
    let secp256k1_signature: Signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered public key pair.
    let secp256k1_public_key = secp256k1_signature.recover(signed_message);
    assert(secp256k1_public_key.is_ok());
    assert(
        secp256k1_public_key
            .unwrap() == PublicKey::from((
            0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
            0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
        )),
    );

    // Secp256r1 Public Key Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A recovered public key pair.
    let secp256r1_public_key = secp256r1_signature.recover(signed_message);
    assert(secp256r1_public_key.is_ok());
    assert(
        secp256r1_public_key
            .unwrap() == PublicKey::from((
            0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
            0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
        )),
    );
    // ANCHOR_END: public_key_recovery
}

fn address_recovery() {
    // ANCHOR: address_recovery
    // Secp256k1 Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered Fuel address.
    let secp256k1_address = secp256k1_signature.address(signed_message);
    assert(secp256k1_address.is_ok());
    assert(
        secp256k1_address
            .unwrap() == Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881),
    );

    // Secp256r1 Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered Fuel address.
    let secp256r1_address = secp256r1_signature.address(signed_message);
    assert(secp256r1_address.is_ok());
    assert(
        secp256r1_address
            .unwrap() == Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a),
    );

    // ANCHOR_END: address_recovery
}

fn evm_address_recovery() {
    // ANCHOR: evm_address_recovery
    // Secp256k1 EVM Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_evm_address = secp256k1_signature.evm_address(signed_message);
    assert(secp256k1_evm_address.is_ok());
    assert(
        secp256k1_evm_address
            .unwrap() == EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb),
    );

    // Secp256r1 EVM Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_evm_address = secp256r1_signature.evm_address(signed_message);
    assert(secp256r1_evm_address.is_ok());
    assert(
        secp256r1_evm_address
            .unwrap() == EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a),
    );
    // ANCHOR_END: evm_address_recovery
}

fn signature_verification() {
    // ANCHOR: signature_verification
    // Secp256k1 Signature Verification
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let secp256k1_public_key = PublicKey::from((
        0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
        0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified public key
    let secp256k1_verified = secp256k1_signature.verify(secp256k1_public_key, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Signature Verification
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let secp256r1_public_key = PublicKey::from((
        0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
        0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
    ));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A verified public key 
    let secp256r1_verified = secp256r1_signature.verify(secp256r1_public_key, signed_message);
    assert(secp256r1_verified.is_ok());

    // Ed25519 Signature Verification
    let ed25519_public_key = PublicKey::from(0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10);
    let ed25519_signature = Signature::Ed25519(Ed25519::from((
        0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545,
        0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00,
    )));
    let hashed_message = Message::from(sha256(b256::zero()));
    // A verified public key  
    let ed25519_verified = ed25519_signature.verify(ed25519_public_key, hashed_message);
    assert(ed25519_verified.is_ok());
    // ANCHOR_END: signature_verification
}

fn address_verification() {
    // ANCHOR: address_verification
    // Secp256k1 Address Verification
    let secp256k1_address = Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881);
    let secp256k1_signature = Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified address
    let secp256k1_verified = secp256k1_signature.verify_address(secp256k1_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A verified address
    let secp256r1_verified = secp256r1_signature.verify_address(secp256r1_address, signed_message);
    assert(secp256r1_verified.is_ok());

    // ANCHOR_END: address_verification
}

fn evm_address_verification() {
    // ANCHOR: evm_address_verification
    // Secp256k1 Address Verification
    let secp256k1_evm_address = EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb);
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_verified = secp256k1_signature.verify_evm_address(secp256k1_evm_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_evm_address = EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_verified = secp256r1_signature.verify_evm_address(secp256r1_evm_address, signed_message);
    assert(secp256r1_verified.is_ok());
    // ANCHOR_END: evm_address_verification
}\n```

Address Signature Verification

Given a Signature, Address, and Message, you can verify that the message was signed by the address.

```sway\nscript;

fn main() {}

use std::{
    crypto::{
        ed25519::*,
        message::*,
        public_key::*,
        secp256k1::*,
        secp256r1::*,
        signature::*,
    },
    hash::{
        Hash,
        sha256,
    },
    vm::evm::evm_address::EvmAddress,
};

fn public_key_recovery() {
    // ANCHOR: public_key_recovery
    // Secp256rk1 Public Key Recovery
    let secp256k1_signature: Signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered public key pair.
    let secp256k1_public_key = secp256k1_signature.recover(signed_message);
    assert(secp256k1_public_key.is_ok());
    assert(
        secp256k1_public_key
            .unwrap() == PublicKey::from((
            0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
            0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
        )),
    );

    // Secp256r1 Public Key Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A recovered public key pair.
    let secp256r1_public_key = secp256r1_signature.recover(signed_message);
    assert(secp256r1_public_key.is_ok());
    assert(
        secp256r1_public_key
            .unwrap() == PublicKey::from((
            0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
            0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
        )),
    );
    // ANCHOR_END: public_key_recovery
}

fn address_recovery() {
    // ANCHOR: address_recovery
    // Secp256k1 Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered Fuel address.
    let secp256k1_address = secp256k1_signature.address(signed_message);
    assert(secp256k1_address.is_ok());
    assert(
        secp256k1_address
            .unwrap() == Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881),
    );

    // Secp256r1 Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered Fuel address.
    let secp256r1_address = secp256r1_signature.address(signed_message);
    assert(secp256r1_address.is_ok());
    assert(
        secp256r1_address
            .unwrap() == Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a),
    );

    // ANCHOR_END: address_recovery
}

fn evm_address_recovery() {
    // ANCHOR: evm_address_recovery
    // Secp256k1 EVM Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_evm_address = secp256k1_signature.evm_address(signed_message);
    assert(secp256k1_evm_address.is_ok());
    assert(
        secp256k1_evm_address
            .unwrap() == EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb),
    );

    // Secp256r1 EVM Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_evm_address = secp256r1_signature.evm_address(signed_message);
    assert(secp256r1_evm_address.is_ok());
    assert(
        secp256r1_evm_address
            .unwrap() == EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a),
    );
    // ANCHOR_END: evm_address_recovery
}

fn signature_verification() {
    // ANCHOR: signature_verification
    // Secp256k1 Signature Verification
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let secp256k1_public_key = PublicKey::from((
        0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
        0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified public key
    let secp256k1_verified = secp256k1_signature.verify(secp256k1_public_key, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Signature Verification
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let secp256r1_public_key = PublicKey::from((
        0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
        0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
    ));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A verified public key 
    let secp256r1_verified = secp256r1_signature.verify(secp256r1_public_key, signed_message);
    assert(secp256r1_verified.is_ok());

    // Ed25519 Signature Verification
    let ed25519_public_key = PublicKey::from(0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10);
    let ed25519_signature = Signature::Ed25519(Ed25519::from((
        0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545,
        0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00,
    )));
    let hashed_message = Message::from(sha256(b256::zero()));
    // A verified public key  
    let ed25519_verified = ed25519_signature.verify(ed25519_public_key, hashed_message);
    assert(ed25519_verified.is_ok());
    // ANCHOR_END: signature_verification
}

fn address_verification() {
    // ANCHOR: address_verification
    // Secp256k1 Address Verification
    let secp256k1_address = Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881);
    let secp256k1_signature = Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified address
    let secp256k1_verified = secp256k1_signature.verify_address(secp256k1_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A verified address
    let secp256r1_verified = secp256r1_signature.verify_address(secp256r1_address, signed_message);
    assert(secp256r1_verified.is_ok());

    // ANCHOR_END: address_verification
}

fn evm_address_verification() {
    // ANCHOR: evm_address_verification
    // Secp256k1 Address Verification
    let secp256k1_evm_address = EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb);
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_verified = secp256k1_signature.verify_evm_address(secp256k1_evm_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_evm_address = EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_verified = secp256r1_signature.verify_evm_address(secp256r1_evm_address, signed_message);
    assert(secp256r1_verified.is_ok());
    // ANCHOR_END: evm_address_verification
}\n```

EVM Address Signature Verification

Recovery of EVM addresses verification is also supported.

```sway\nscript;

fn main() {}

use std::{
    crypto::{
        ed25519::*,
        message::*,
        public_key::*,
        secp256k1::*,
        secp256r1::*,
        signature::*,
    },
    hash::{
        Hash,
        sha256,
    },
    vm::evm::evm_address::EvmAddress,
};

fn public_key_recovery() {
    // ANCHOR: public_key_recovery
    // Secp256rk1 Public Key Recovery
    let secp256k1_signature: Signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered public key pair.
    let secp256k1_public_key = secp256k1_signature.recover(signed_message);
    assert(secp256k1_public_key.is_ok());
    assert(
        secp256k1_public_key
            .unwrap() == PublicKey::from((
            0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
            0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
        )),
    );

    // Secp256r1 Public Key Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A recovered public key pair.
    let secp256r1_public_key = secp256r1_signature.recover(signed_message);
    assert(secp256r1_public_key.is_ok());
    assert(
        secp256r1_public_key
            .unwrap() == PublicKey::from((
            0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
            0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
        )),
    );
    // ANCHOR_END: public_key_recovery
}

fn address_recovery() {
    // ANCHOR: address_recovery
    // Secp256k1 Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A recovered Fuel address.
    let secp256k1_address = secp256k1_signature.address(signed_message);
    assert(secp256k1_address.is_ok());
    assert(
        secp256k1_address
            .unwrap() == Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881),
    );

    // Secp256r1 Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered Fuel address.
    let secp256r1_address = secp256r1_signature.address(signed_message);
    assert(secp256r1_address.is_ok());
    assert(
        secp256r1_address
            .unwrap() == Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a),
    );

    // ANCHOR_END: address_recovery
}

fn evm_address_recovery() {
    // ANCHOR: evm_address_recovery
    // Secp256k1 EVM Address Recovery
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_evm_address = secp256k1_signature.evm_address(signed_message);
    assert(secp256k1_evm_address.is_ok());
    assert(
        secp256k1_evm_address
            .unwrap() == EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb),
    );

    // Secp256r1 EVM Address Recovery
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_evm_address = secp256r1_signature.evm_address(signed_message);
    assert(secp256r1_evm_address.is_ok());
    assert(
        secp256r1_evm_address
            .unwrap() == EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a),
    );
    // ANCHOR_END: evm_address_recovery
}

fn signature_verification() {
    // ANCHOR: signature_verification
    // Secp256k1 Signature Verification
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    )));
    let secp256k1_public_key = PublicKey::from((
        0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
        0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified public key
    let secp256k1_verified = secp256k1_signature.verify(secp256k1_public_key, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Signature Verification
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
        0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
    )));
    let secp256r1_public_key = PublicKey::from((
        0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
        0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
    ));
    let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
    // A verified public key 
    let secp256r1_verified = secp256r1_signature.verify(secp256r1_public_key, signed_message);
    assert(secp256r1_verified.is_ok());

    // Ed25519 Signature Verification
    let ed25519_public_key = PublicKey::from(0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10);
    let ed25519_signature = Signature::Ed25519(Ed25519::from((
        0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545,
        0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00,
    )));
    let hashed_message = Message::from(sha256(b256::zero()));
    // A verified public key  
    let ed25519_verified = ed25519_signature.verify(ed25519_public_key, hashed_message);
    assert(ed25519_verified.is_ok());
    // ANCHOR_END: signature_verification
}

fn address_verification() {
    // ANCHOR: address_verification
    // Secp256k1 Address Verification
    let secp256k1_address = Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881);
    let secp256k1_signature = Secp256k1::from((
        0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
        0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
    ));
    let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
    // A verified address
    let secp256k1_verified = secp256k1_signature.verify_address(secp256k1_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A verified address
    let secp256r1_verified = secp256r1_signature.verify_address(secp256r1_address, signed_message);
    assert(secp256r1_verified.is_ok());

    // ANCHOR_END: address_verification
}

fn evm_address_verification() {
    // ANCHOR: evm_address_verification
    // Secp256k1 Address Verification
    let secp256k1_evm_address = EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb);
    let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
        0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
        0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
    )));
    let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
    // A recovered EVM address.
    let secp256k1_verified = secp256k1_signature.verify_evm_address(secp256k1_evm_address, signed_message);
    assert(secp256k1_verified.is_ok());

    // Secp256r1 Address Verification
    let secp256r1_evm_address = EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a);
    let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
        0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
        0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
    )));
    let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
    // A recovered EVM address.
    let secp256r1_verified = secp256r1_signature.verify_evm_address(secp256r1_evm_address, signed_message);
    assert(secp256r1_verified.is_ok());
    // ANCHOR_END: evm_address_verification
}\n```

Storage

When developing a smart contract, you will typically need some sort of persistent storage. In this case, persistent storage, often just called storage in this context, is a place where you can store values that are persisted inside the contract itself. This is in contrast to a regular value in memory, which disappears after the contract exits.

Put in conventional programming terms, contract storage is like saving data to a hard drive. That data is saved even after the program that saved it exits. That data is persistent. Using memory is like declaring a variable in a program: it exists for the duration of the program and is non-persistent.

Some basic use cases of storage include declaring an owner address for a contract and saving balances in a wallet.

Storage Accesses Via the storage Keyword

Declaring variables in storage requires a storage block that contains a list of all your variables, their types, and their initial values. The initial value can be any expression that can be evaluated to a constant during compilation, as follows:

```sway\ncontract;

// ANCHOR: basic_storage_declaration
storage {
    var1: u64 = 1,
    var2: b256 = b256::zero(),
    var3: Address = Address::zero(),
    var4: Option<u8> = None,
}
// ANCHOR_END: basic_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_something();

    #[storage(read)]
    fn get_something();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_something() {
        // ANCHOR: basic_storage_write
        storage.var1.write(42);
        storage
            .var2
            .write(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .var3
            .write(Address::from(0x1111111111111111111111111111111111111111111111111111111111111111));
        storage.var4.write(Some(2u8));
        // ANCHOR_END: basic_storage_write
    }
    #[storage(read)]
    fn get_something() {
        // ANCHOR: basic_storage_read
        let var1: u64 = storage.var1.read();
        let var2: b256 = storage.var2.try_read().unwrap_or(b256::zero());
        let var3: Address = storage.var3.try_read().unwrap_or(Address::zero());
        let var4: Option<u8> = storage.var4.try_read().unwrap_or(None);
        // ANCHOR_END: basic_storage_read
    }
}\n```

To write into a storage variable, you need to use the storage keyword as follows:

```sway\ncontract;

// ANCHOR: basic_storage_declaration
storage {
    var1: u64 = 1,
    var2: b256 = b256::zero(),
    var3: Address = Address::zero(),
    var4: Option<u8> = None,
}
// ANCHOR_END: basic_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_something();

    #[storage(read)]
    fn get_something();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_something() {
        // ANCHOR: basic_storage_write
        storage.var1.write(42);
        storage
            .var2
            .write(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .var3
            .write(Address::from(0x1111111111111111111111111111111111111111111111111111111111111111));
        storage.var4.write(Some(2u8));
        // ANCHOR_END: basic_storage_write
    }
    #[storage(read)]
    fn get_something() {
        // ANCHOR: basic_storage_read
        let var1: u64 = storage.var1.read();
        let var2: b256 = storage.var2.try_read().unwrap_or(b256::zero());
        let var3: Address = storage.var3.try_read().unwrap_or(Address::zero());
        let var4: Option<u8> = storage.var4.try_read().unwrap_or(None);
        // ANCHOR_END: basic_storage_read
    }
}\n```

To read a storage variable, you also need to use the storage keyword. You may use read() or try_read(), however we recommend using try_read() for additional safety.

```sway\ncontract;

// ANCHOR: basic_storage_declaration
storage {
    var1: u64 = 1,
    var2: b256 = b256::zero(),
    var3: Address = Address::zero(),
    var4: Option<u8> = None,
}
// ANCHOR_END: basic_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_something();

    #[storage(read)]
    fn get_something();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_something() {
        // ANCHOR: basic_storage_write
        storage.var1.write(42);
        storage
            .var2
            .write(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .var3
            .write(Address::from(0x1111111111111111111111111111111111111111111111111111111111111111));
        storage.var4.write(Some(2u8));
        // ANCHOR_END: basic_storage_write
    }
    #[storage(read)]
    fn get_something() {
        // ANCHOR: basic_storage_read
        let var1: u64 = storage.var1.read();
        let var2: b256 = storage.var2.try_read().unwrap_or(b256::zero());
        let var3: Address = storage.var3.try_read().unwrap_or(Address::zero());
        let var4: Option<u8> = storage.var4.try_read().unwrap_or(None);
        // ANCHOR_END: basic_storage_read
    }
}\n```

Storing Structs

To store a struct in storage, each variable must be assigned in the storage block. This can be either my assigning the fields individually or using a public constructor that can be evaluated to a constant during compilation.

```sway\ncontract;

// ANCHOR: struct_storage_declaration
struct Type1 {
    x: u64,
    y: u64,
}

struct Type2 {
    w: b256,
    z: bool,
}

impl Type2 {
    // a constructor that evaluates to a constant during compilation
    fn default() -> Self {
        Self {
            w: 0x0000000000000000000000000000000000000000000000000000000000000000,
            z: true,
        }
    }
}

storage {
    var1: Type1 = Type1 { x: 0, y: 0 },
    var2: Type2 = Type2::default(),
}
// ANCHOR_END: struct_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_struct();

    #[storage(read)]
    fn get_struct();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_struct() {
        // ANCHOR: struct_storage_write
        // Store individual fields
        storage.var1.x.write(42);
        storage.var1.y.write(77);

        // Store an entire struct
        let new_struct = Type2 {
            w: 0x1111111111111111111111111111111111111111111111111111111111111111,
            z: false,
        };
        storage.var2.write(new_struct);
        // ANCHOR_END: struct_storage_write
    }

    #[storage(read)]
    fn get_struct() {
        // ANCHOR: struct_storage_read
        let var1_x: u64 = storage.var1.x.try_read().unwrap_or(0);
        let var1_y: u64 = storage.var1.y.try_read().unwrap_or(0);
        let var2: Type2 = storage.var2.try_read().unwrap_or(Type2::default());
        // ANCHOR_END: struct_storage_read
    }
}\n```

You may write to both fields of a struct and the entire struct as follows:

```sway\ncontract;

// ANCHOR: struct_storage_declaration
struct Type1 {
    x: u64,
    y: u64,
}

struct Type2 {
    w: b256,
    z: bool,
}

impl Type2 {
    // a constructor that evaluates to a constant during compilation
    fn default() -> Self {
        Self {
            w: 0x0000000000000000000000000000000000000000000000000000000000000000,
            z: true,
        }
    }
}

storage {
    var1: Type1 = Type1 { x: 0, y: 0 },
    var2: Type2 = Type2::default(),
}
// ANCHOR_END: struct_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_struct();

    #[storage(read)]
    fn get_struct();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_struct() {
        // ANCHOR: struct_storage_write
        // Store individual fields
        storage.var1.x.write(42);
        storage.var1.y.write(77);

        // Store an entire struct
        let new_struct = Type2 {
            w: 0x1111111111111111111111111111111111111111111111111111111111111111,
            z: false,
        };
        storage.var2.write(new_struct);
        // ANCHOR_END: struct_storage_write
    }

    #[storage(read)]
    fn get_struct() {
        // ANCHOR: struct_storage_read
        let var1_x: u64 = storage.var1.x.try_read().unwrap_or(0);
        let var1_y: u64 = storage.var1.y.try_read().unwrap_or(0);
        let var2: Type2 = storage.var2.try_read().unwrap_or(Type2::default());
        // ANCHOR_END: struct_storage_read
    }
}\n```

The same applies to reading structs from storage, where both the individual and struct as a whole may be read as follows:

```sway\ncontract;

// ANCHOR: struct_storage_declaration
struct Type1 {
    x: u64,
    y: u64,
}

struct Type2 {
    w: b256,
    z: bool,
}

impl Type2 {
    // a constructor that evaluates to a constant during compilation
    fn default() -> Self {
        Self {
            w: 0x0000000000000000000000000000000000000000000000000000000000000000,
            z: true,
        }
    }
}

storage {
    var1: Type1 = Type1 { x: 0, y: 0 },
    var2: Type2 = Type2::default(),
}
// ANCHOR_END: struct_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_struct();

    #[storage(read)]
    fn get_struct();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_struct() {
        // ANCHOR: struct_storage_write
        // Store individual fields
        storage.var1.x.write(42);
        storage.var1.y.write(77);

        // Store an entire struct
        let new_struct = Type2 {
            w: 0x1111111111111111111111111111111111111111111111111111111111111111,
            z: false,
        };
        storage.var2.write(new_struct);
        // ANCHOR_END: struct_storage_write
    }

    #[storage(read)]
    fn get_struct() {
        // ANCHOR: struct_storage_read
        let var1_x: u64 = storage.var1.x.try_read().unwrap_or(0);
        let var1_y: u64 = storage.var1.y.try_read().unwrap_or(0);
        let var2: Type2 = storage.var2.try_read().unwrap_or(Type2::default());
        // ANCHOR_END: struct_storage_read
    }
}\n```

Common Storage Collections

We support the following common storage collections:

  • StorageMap<K, V>
  • StorageVec<T>
  • StorageBytes
  • StorageString

Please note that these types are not initialized during compilation. This means that if you try to access a key from a storage map before the storage has been set, for example, the call will revert.

Declaring these variables in storage requires a storage block as follows:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

StorageMaps<K, V>

Generic storage maps are available in the standard library as StorageMap<K, V> which have to be defined inside a storage block and allow you to call insert() and get() to insert values at specific keys and get those values respectively. Refer to Storage Maps for more information about StorageMap<K, V>.

Warning While the StorageMap<K, V> is currently included in the prelude, to use it the Hash trait must still be imported. This is a known issue and will be resolved.

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

To write to a storage map, call either the insert() or try_insert() functions as follows:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

The following demonstrates how to read from a storage map:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

StorageVec<T>

Generic storage vectors are available in the standard library as StorageVec<T> which have to be defined inside a storage block and allow you to call push() and pop() to push and pop values from a vector respectively. Refer to Storage Vector for more information about StorageVec<T>.

The following demonstrates how to import StorageVec<T>:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

NOTE: When importing the StorageVec<T>, please be sure to use the glob operator: use std::storage::storage_vec::*.

The following demonstrates how to write to a StorageVec<T>:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

The following demonstrates how to read from a StorageVec<T>:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

StorageBytes

Storage of Bytes is available in the standard library as StorageBytes which have to be defined inside a storage block. StorageBytes cannot be manipulated in the same way a StorageVec<T> or StorageMap<K, V> can but stores bytes more efficiently thus reducing gas. Only the entirety of a Bytes may be read/written to storage. This means any changes would require loading the entire Bytes to the heap, making changes, and then storing it once again. If frequent changes are needed, a StorageVec<u8> is recommended.

The following demonstrates how to import StorageBytes:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

NOTE: When importing the StorageBytes, please be sure to use the glob operator: use std::storage::storage_bytes::*.

The following demonstrates how to write to a StorageBytes:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

The following demonstrates how to read from a StorageBytes:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

StorageString

Storage of String is available in the standard library as StorageString which have to be defined inside a storage block. StorageString cannot be manipulated in the same way a StorageVec<T> or StorageMap<K, V>. Only the entirety of a String may be read/written to storage.

The following demonstrates how to import StorageString:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

NOTE: When importing the StorageString, please be sure to use the glob operator: use std::storage::storage_string::*.

The following demonstrates how to write to a StorageString:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

The following demonstrates how to read from a StorageString:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: temp_hash_import
use std::hash::Hash;
// ANCHOR: temp_hash_import

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR: storage_vec_import

// ANCHOR: storage_bytes_import
use std::storage::storage_bytes::*;
// ANCHOR: storage_bytes_import

// ANCHOR: storage_string_import
use std::storage::storage_string::*;
// ANCHOR: storage_string_import

// ANCHOR: advanced_storage_declaration
storage {
    storage_map: StorageMap<u64, bool> = StorageMap {},
    storage_vec: StorageVec<b256> = StorageVec {},
    storage_string: StorageString = StorageString {},
    storage_bytes: StorageBytes = StorageBytes {},
}
// ANCHOR_END: advanced_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map();
    #[storage(read)]
    fn get_map();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
    #[storage(write)]
    fn store_string();
    #[storage(read)]
    fn get_string();
    #[storage(write)]
    fn store_bytes();
    #[storage(read)]
    fn get_bytes();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map() {
        // ANCHOR: map_storage_write
        storage.storage_map.insert(12, true);
        storage.storage_map.insert(59, false);

        // try_insert() will only insert if a value does not already exist for a key.
        let result = storage.storage_map.try_insert(103, true);
        assert(result.is_ok());
        // ANCHOR_END: map_storage_write
    }
    #[storage(read)]
    fn get_map() {
        // ANCHOR: map_storage_read
        // Access directly
        let stored_val1: bool = storage.storage_map.get(12).try_read().unwrap_or(false);

        // First get the storage key and then access the value.
        let storage_key2: StorageKey<bool> = storage.storage_map.get(59);
        let stored_val2: bool = storage_key2.try_read().unwrap_or(false);

        // Unsafely access the value.
        let stored_val3: bool = storage.storage_map.get(103).read();
        // ANCHOR_END: map_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: vec_storage_write
        storage
            .storage_vec
            .push(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000001);
        storage
            .storage_vec
            .push(0x0000000000000000000000000000000000000000000000000000000000000002);

        // Set will overwrite the element stored at the given index.
        storage.storage_vec.set(2, b256::zero());
        // ANCHOR_END: vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: vec_storage_read
        // Method 1: Access the element directly
        // Note: get() does not remove the element from the vec.
        let stored_val1: b256 = storage.storage_vec.get(0).unwrap().try_read().unwrap_or(b256::zero());

        // Method 2: First get the storage key and then access the value.
        let storage_key2: StorageKey<b256> = storage.storage_vec.get(1).unwrap();
        let stored_val2: b256 = storage_key2.try_read().unwrap_or(b256::zero());

        // pop() will remove the last element from the vec.
        let length: u64 = storage.storage_vec.len();
        let stored_val3: b256 = storage.storage_vec.pop().unwrap();
        assert(length != storage.storage_vec.len());
        // ANCHOR_END: vec_storage_read
    }

    #[storage(write)]
    fn store_string() {
        // ANCHOR: string_storage_write
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.storage_string.write_slice(my_string);
        // ANCHOR_END: string_storage_write
    }
    #[storage(read)]
    fn get_string() {
        // ANCHOR: string_storage_read
        let stored_string: String = storage.storage_string.read_slice().unwrap();
        // ANCHOR_END: string_storage_read
    }

    #[storage(write)]
    fn store_bytes() {
        // ANCHOR: bytes_storage_write
        // Setup Bytes
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Write to storage
        storage.storage_bytes.write_slice(my_bytes);
        // ANCHOR_END: bytes_storage_write
    }
    #[storage(read)]
    fn get_bytes() {
        // ANCHOR: bytes_storage_read
        let stored_bytes: Bytes = storage.storage_bytes.read_slice().unwrap();
        // ANCHOR_END: bytes_storage_read
    }
}\n```

Advanced Storage

For more advanced storage techniques please refer to the Advanced Storage page.

Purity

A function is pure if it does not access any persistent storage. Conversely, the function is impure if it does access any storage. Naturally, as storage is only available in smart contracts, impure functions cannot be used in predicates, scripts, or libraries. A pure function cannot call an impure function.

In Sway, functions are pure by default but can be opted into impurity via the storage function attribute. The storage attribute may take read and/or write arguments indicating which type of access the function requires.

The storage attribute without any arguments, #[storage()], indicates a pure function, and has the same effect as not having the attribute at all.

#[storage(read)]
fn get_amount() -> u64 {
    ...
}

#[storage(read, write)]
fn increment_amount(increment: u64) -> u64 {
    ...
}

fn a_pure_function() {
    ...
}

#[storage()]
fn also_a_pure_function() {
    ...
}

Note: the #[storage(write)] attribute also permits a function to read from storage. This is due to the fact that partially writing a storage slot requires first reading the slot.

Impure functions which call other impure functions must have at least the same storage privileges or a superset of those for the function called. For example, to call a function with write access a caller must also have write access, or both read and write access. To call a function with read and write access the caller must also have both privileges.

The storage attribute may also be applied to methods and associated functions, trait and ABI declarations.

A pure function gives you some guarantees: you will not incur excessive storage gas costs, the compiler can apply additional optimizations, and they are generally easy to reason about and audit.

Note: Purity does not provide an absolute guarantee that a storage access will not happen as a result of calling a pure function. E.g., it is possible for a pure function to call another contract, which can then call a write function in the original contract. The guarantee that the purity gives in this example is, that the original pure function itself does not change the storage, as well as that any function later called, that accesses storage, is clearly marked as impure.

A similar concept exists in Solidity. Note that Solidity refers to contract storage as contract state, and in the Sway/Fuel ecosystem, these two terms are largely interchangeable.

Identifiers

Addresses in Sway are similar to EVM addresses. The two major differences are:

  1. Sway addresses are 32 bytes long (instead of 20)
  2. Sway addresses are computed with the SHA-256 hash of the public key instead of the keccak-256 hash.

Contracts, on the other hand, are uniquely identified with a contract ID rather than an address. A contract's ID is also 32 bytes long and is calculated here.

Native Assets

The FuelVM has built-in support for working with multiple assets.

Key Differences Between EVM and FuelVM Assets

ERC-20 vs Native Asset

On the EVM, Ether is the native asset. As such, sending ETH to an address or contract is an operation built into the EVM, meaning it doesn't rely on the existence of a smart contract to update balances to track ownership as with ERC-20 tokens.

On the FuelVM, all assets are native and the process for sending any native asset is the same.

While you would still need a smart contract to handle the minting and burning of assets, the sending and receiving of these assets can be done independently of the asset contract.

Just like the EVM however, Fuel has a standard that describes a standard API for Native Assets using the Sway Language. The ERC-20 equivalent for the Sway Language is the SRC-20; Native Asset Standard.

NOTE It is important to note that Fuel does not have tokens.

ERC-721 vs Native Asset

On the EVM, an ERC-721 token or NFT is a contract that contains multiple tokens which are non-fungible with one another.

On the FuelVM, the ERC-721 equivalent is a Native Asset where each asset has a supply of one. This is defined in the SRC-20; Native Asset Standard under the Non-Fungible Asset Restrictions.

In practice, this means all NFTs are treated the same as any other Native Asset on Fuel. When writing Sway code, no additional cases for handling non-fungible and fungible assets are required.

No Token Approvals

An advantage Native Assets bring is that there is no need for token approvals; as with Ether on the EVM. With millions of dollars hacked every year due to misused token approvals, the FuelVM eliminates this attack vector.

Asset vs Coin vs Token

An "Asset" is a Native Asset on Fuel and has the associated AssetId type. Assets are distinguishable from one another. A "Coin" represents a singular unit of an Asset. Coins of the same Asset are not distinguishable from one another.

Fuel does not use tokens like other ecosystems such as Ethereum and uses Native Assets with a UTXO design instead.

The AssetId type

The AssetId type represents any Native Asset on Fuel. An AssetId is used for interacting with an asset on the network.

The AssetId of any Native Asset on Fuel is calculated by taking the SHA256 hash digest of the originating ContractId that minted the asset and a SubId i.e. sha256((contract_id, sub_id)).

Creating a New AssetId

There are 3 ways to instantiate a new AssetId:

Default

When a contract will only ever mint a single asset, it is recommended to use the DEFAULT_ASSET_ID sub id. This is referred to as the default asset of a contract.

To get the default asset from an internal contract call, call the default() function:

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

New

If a contract mints multiple assets or if the asset has been minted by an external contract, the new() function will be needed. The new() function takes the ContractId of the contract which minted the token as well as a SubId.

To create a new AssetId using a ContractId and SubId, call the new() function:

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

From

In the case where the b256 value of an asset is already known, you may call the from() function with the b256 value.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

The SubId type

The SubId is used to differentiate between different assets that are created by the same contract. The SubId is a b256 value.

When creating a single new asset on Fuel, we recommend using the DEFAULT_SUB_ID or SubId::zero().

The Base Asset

On the Fuel Network, the base asset is Ether. This is the only asset on the Fuel Network that does not have a SubId.

The Base Asset can be returned anytime by calling the base() function of the AssetId type.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

Basic Native Asset Functionality

Minting A Native Asset

To mint a new asset, the std::asset::mint() function must be called internally within a contract. A SubId and amount of coins must be provided. These newly minted coins will be owned by the contract which minted them. To mint another asset from the same contract, replace the DEFAULT_SUB_ID with your desired SubId.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

You may also mint an asset to a specific entity with the std::asset::mint_to() function. Be sure to provide a target Identity that will own the newly minted coins.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

If you intend to allow external users to mint assets using your contract, the SRC-3; Mint and Burn Standard defines a standard API for minting assets. The Sway-Libs Asset Library also provides an additional library to support implementations of the SRC-3 Standard into your contract.

Burning a Native Asset

To burn an asset, the std::asset::burn() function must be called internally from the contract which minted them. The SubId used to mint the coins and amount must be provided. The burned coins must be owned by the contract. When an asset is burned it doesn't exist anymore.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

If you intend to allow external users to burn assets using your contract, the SRC-3; Mint and Burn Standard defines a standard API for burning assets. The Sway-Libs Asset Library also provides an additional library to support implementations of the SRC-3 Standard into your contract.

Transfer a Native Asset

To internally transfer a Native Asset, the std::asset::transfer() function must be called. A target Identity or user must be provided as well as the AssetId of the asset and an amount.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

Native Asset And Transactions

Getting The Transaction Asset

To query for the Native Asset sent in a transaction, you may call the std::call_frames::msg_asset_id() function.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

Getting The Transaction Amount

To query for the amount of coins sent in a transaction, you may call the std::context::msg_amount() function.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

Native Assets and Contracts

Checking A Contract's Balance

To internally check a contract's balance, call the std::context::this_balance() function with the corresponding AssetId.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

To check the balance of an external contract, call the std::context::balance_of() function with the corresponding AssetId.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

NOTE Due to the FuelVM's UTXO design, balances of Address's cannot be returned in the Sway Language. This must be done off-chain using the SDK.

Receiving Native Assets In A Contract

By default, a contract may not receive a Native Asset in a contract call. To allow transferring of assets to the contract, add the #[payable] attribute to the function.

```sway\ncontract;

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

abi NativeAsset {
    fn mint_coins(mint_amount: u64);
    fn burn_coins(burn_amount: u64);
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    #[payable]
    fn deposit();
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    fn get_msg_amount();
    fn this_balance(asset_id: AssetId) -> u64;
    fn get_msg_asset_id();
    fn mint_coins_to(target_identity: Identity, mint_amount: u64);
}

impl NativeAsset for Contract {
    /// Mint an amount of this contracts native asset to the contracts balance.
    fn mint_coins(mint_amount: u64) {
        // ANCHOR: mint_asset
        mint(DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_asset
    }

    fn mint_coins_to(target_identity: Identity, mint_amount: u64) {
        // ANCHOR: mint_to_asset
        mint_to(target_identity, DEFAULT_SUB_ID, mint_amount);
        // ANCHOR_END: mint_to_asset
    }

    /// Burn an amount of this contracts native asset.
    fn burn_coins(burn_amount: u64) {
        // ANCHOR: burn_asset
        burn(DEFAULT_SUB_ID, burn_amount);
        // ANCHOR_END: burn_asset
    }

    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
        // ANCHOR: transfer_asset
        transfer(target, asset_id, coins);
        // ANCHOR_END: transfer_asset
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn get_balance(target_contract: ContractId, asset_id: AssetId) -> u64 {
        // ANCHOR: balance_of
        balance_of(target_contract, asset_id)
        // ANCHOR_END: balance_of
    }

    /// Get the internal balance of a specific coin at a specific contract.
    fn this_balance(asset_id: AssetId) -> u64 {
        // ANCHOR: this_balance
        this_balance(asset_id)
        // ANCHOR_END: this_balance
    }

    /// Deposit coins back into the contract.
    // ANCHOR: payable
    #[payable]
    fn deposit() {
        assert(msg_amount() > 0);
    }
    // ANCHOR_END: payable
    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_amount() {
        // ANCHOR: msg_amount
        let amount = msg_amount();
        // ANCHOR_END: msg_amount
    }

    /// Mint and send this contracts native asset to a destination contract.
    fn get_msg_asset_id() {
        // ANCHOR: msg_asset_id
        let amount = msg_asset_id();
        // ANCHOR_END: msg_asset_id
    }
}

fn get_base_asset() {
    // ANCHOR: base_asset
    let base_asset: AssetId = AssetId::base();
    // ANCHOR_END: base_asset
}

fn default_asset_id() {
    // ANCHOR: default_asset_id
    let asset_id: AssetId = AssetId::default();
    // ANCHOR_END: default_asset_id
}

fn new_asset_id(my_contract_id: ContractId, my_sub_id: SubId) {
    // ANCHOR: new_asset_id
    let my_contract_id: ContractId = ContractId::from(0x1000000000000000000000000000000000000000000000000000000000000000);
    let my_sub_id: SubId = 0x2000000000000000000000000000000000000000000000000000000000000000;

    let asset_id: AssetId = AssetId::new(my_contract_id, my_sub_id);
    // ANCHOR_END: new_asset_id
}

fn from_asset_id() {
    // ANCHOR: from_asset_id
    let asset_id: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
    // ANCHOR_END: from_asset_id
}\n```

Native Asset Standards

There are a number of standards developed to enable further functionality for Native Assets and help cross contract functionality. Information on standards can be found in the Sway Standards Repo.

We currently have the following standards for Native Assets:

Native Asset Libraries

Additional Libraries have been developed to allow you to quickly create an deploy dApps that follow the Sway Standards.

Single Native Asset Example

In this fully fleshed out example, we show a native asset contract which mints a single asset. This is the equivalent to the ERC-20 Standard use in Ethereum. Note there are no token approval functions.

It implements the SRC-20; Native Asset, SRC-3; Mint and Burn, and SRC-5; Ownership standards. It does not use any external libraries.

// ERC20 equivalent in Sway.
contract;

use standards::{
    src3::SRC3,
    src5::{
        SRC5, 
        State, 
        AccessError,
    },
    src20::{
        SetDecimalsEvent, 
        SetNameEvent, 
        SetSymbolEvent, 
        SRC20, 
        TotalSupplyEvent,
    },
};
use std::{
    asset::{
        burn,
        mint_to,
    },
    call_frames::msg_asset_id,
    constants::DEFAULT_SUB_ID,
    context::msg_amount,
    string::String,
    contract_id::ContractId
};

configurable {
    DECIMALS: u8 = 9u8,
    NAME: str[7] = __to_str_array("MyAsset"),
    SYMBOL: str[5] = __to_str_array("MYTKN"),
}

storage {
    total_supply: u64 = 0,
    owner: State = State::Uninitialized,
}

// Native Asset Standard
impl SRC20 for Contract {
    #[storage(read)]
    fn total_assets() -> u64 {
        1
    }

    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64> {
        if asset == AssetId::default() {
            Some(storage.total_supply.read())
        } else {
            None
        }
    }

    #[storage(read)]
    fn name(asset: AssetId) -> Option<String> {
        if asset == AssetId::default() {
            Some(String::from_ascii_str(from_str_array(NAME)))
        } else {
            None
        }
    }

    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String> {
        if asset == AssetId::default() {
            Some(String::from_ascii_str(from_str_array(SYMBOL)))
        } else {
            None
        }
    }

    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8> {
        if asset == AssetId::default() {
            Some(DECIMALS)
        } else {
            None
        }
    }
}

// Ownership Standard
impl SRC5 for Contract {
    #[storage(read)]
    fn owner() -> State {
        storage.owner.read()
    }
}

// Mint and Burn Standard
impl SRC3 for Contract {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64) {
        require(sub_id.is_some() && sub_id.unwrap() == DEFAULT_SUB_ID, "incorrect-sub-id");
        require_access_owner();

        let new_supply = storage.total_supply.read() + amount;
        storage
            .total_supply
            .write(new_supply);
        mint_to(recipient, DEFAULT_SUB_ID, amount);
        
        TotalSupplyEvent::new(
            AssetId::default(), 
            new_supply, 
            msg_sender().unwrap()
        ).log();
    }

    #[storage(read, write)]
    fn burn(sub_id: SubId, amount: u64) {
        require(sub_id == DEFAULT_SUB_ID, "incorrect-sub-id");
        require(msg_amount() >= amount, "incorrect-amount-provided");
        require(
            msg_asset_id() == AssetId::default(),
            "incorrect-asset-provided",
        );
        require_access_owner();

        let new_supply = storage.total_supply.read() - amount;
        storage
            .total_supply
            .write(new_supply);
        burn(DEFAULT_SUB_ID, amount);
        
        TotalSupplyEvent::new(
            AssetId::default(), 
            new_supply, 
            msg_sender().unwrap()
        ).log();
    }
}

abi SingleAsset {
    #[storage(read, write)]
    fn constructor(owner_: Identity);
}

impl SingleAsset for Contract {
    #[storage(read, write)]
    fn constructor(owner_: Identity) {
        require(storage.owner.read() == State::Uninitialized, "owner-initialized");
        storage.owner.write(State::Initialized(owner_));
    }
}

#[storage(read)]
fn require_access_owner() {
    require(
        storage.owner.read() == State::Initialized(msg_sender().unwrap()),
        AccessError::NotOwner,
    );
}

abi EmitSRC20Events {
    fn emit_src20_events();
}

impl EmitSRC20Events for Contract {
    fn emit_src20_events() {
        // Metadata that is stored as a configurable should only be emitted once.
        let asset = AssetId::default();
        let sender = msg_sender().unwrap();
        let name = Some(String::from_ascii_str(from_str_array(NAME)));
        let symbol = Some(String::from_ascii_str(from_str_array(SYMBOL)));

        SetNameEvent::new(asset, name, sender).log();
        SetSymbolEvent::new(asset, symbol, sender).log();
        SetDecimalsEvent::new(asset, DECIMALS, sender).log();
    }
}

Multi Native Asset Example

In this fully fleshed out example, we show a native asset contract which mints multiple assets. This is the equivalent to the ERC-1155 Standard use in Ethereum. Note there are no token approval functions.

It implements the SRC-20; Native Asset, SRC-3; Mint and Burn, and SRC-5; Ownership standards. It does not use any external libraries.

// ERC1155 equivalent in Sway.
contract;

use standards::{
    src5::{
        SRC5, 
        State, 
        AccessError
    },
    src20::{
        SetDecimalsEvent, 
        SetNameEvent, 
        SetSymbolEvent, 
        SRC20, 
        TotalSupplyEvent,
    }
    src3::SRC3,
};
use std::{
    asset::{
        burn,
        mint_to,
    },
    call_frames::msg_asset_id,
    hash::{
        Hash,
    },
    context::this_balance,
    storage::storage_string::*,
    string::String,
    contract_id::ContractId
};

storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
    owner: State = State::Uninitialized,
}

// Native Asset Standard
impl SRC20 for Contract {
    #[storage(read)]
    fn total_assets() -> u64 {
        storage.total_assets.read()
    }

    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64> {
        storage.total_supply.get(asset).try_read()
    }

    #[storage(read)]
    fn name(asset: AssetId) -> Option<String> {
        storage.name.get(asset).read_slice()
    }
    
    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String> {
        storage.symbol.get(asset).read_slice()
    }

    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8> {
        storage.decimals.get(asset).try_read()
    }
}

// Mint and Burn Standard
impl SRC3 for Contract {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64) {
        require(sub_id.is_some(), "Error: SubId is None");
        require_access_owner();

        let asset_id = AssetId::new(ContractId::this(), sub_id.unwrap());
        let supply = storage.total_supply.get(asset_id).try_read();
        if supply.is_none() {
            storage.total_assets.write(storage.total_assets.try_read().unwrap_or(0) + 1);
        }
        let new_supply = supply.unwrap_or(0) + amount;
        storage.total_supply.insert(asset_id, new_supply);
        mint_to(recipient, sub_id, amount);
        
        TotalSupplyEvent::new(
            asset_id, 
            new_supply, 
            msg_sender().unwrap()
        ).log();
    }
    
    #[storage(read, write)]
    fn burn(sub_id: SubId, amount: u64) {
        require_access_owner();
        let asset_id = AssetId::new(ContractId::this(), sub_id);
        require(this_balance(asset_id) >= amount, "not-enough-coins");
        
        let supply = storage.total_supply.get(asset_id).try_read();
        let new_supply = supply.unwrap_or(0) - amount;
        storage.total_supply.insert(asset_id, new_supply);
        burn(sub_id, amount);

        TotalSupplyEvent::new(
            asset_id, 
            new_supply, 
            msg_sender().unwrap()
        ).log();
    }
}

abi MultiAsset {
    #[storage(read, write)]
    fn constructor(owner_: Identity);
    
    #[storage(read, write)]
    fn set_name(asset: AssetId, name: Option<String>);

    #[storage(read, write)]
    fn set_symbol(asset: AssetId, symbol: Option<String>);

    #[storage(read, write)]
    fn set_decimals(asset: AssetId, decimals: u8);
}

impl MultiAsset for Contract {
    #[storage(read, write)]
    fn constructor(owner_: Identity) {
        require(storage.owner.read() == State::Uninitialized, "owner-initialized");
        storage.owner.write(State::Initialized(owner_));
    }
    
    #[storage(read, write)]
    fn set_name(asset: AssetId, name: Option<String>) {
        require_access_owner();
        storage.name.insert(asset, StorageString {});
        storage.name.get(asset).write_slice(name);

        SetNameEvent::new(asset, name, msg_sender().unwrap()).log();
    }

    #[storage(read, write)]
    fn set_symbol(asset: AssetId, symbol: Option<String>) {
        require_access_owner();
        storage.symbol.insert(asset, StorageString {});
        storage.symbol.get(asset).write_slice(symbol);

        SetSymbolEvent::new(asset, symbol, msg_sender().unwrap()).log();
    }

    #[storage(read, write)]
    fn set_decimals(asset: AssetId, decimals: u8) {
        require_access_owner();
        storage.decimals.insert(asset, decimals);

        SetDecimalsEvent::new(asset, decimals, msg_sender().unwrap()).log();
    }
}

#[storage(read)]
fn require_access_owner() {
    require(
        storage.owner.read() == State::Initialized(msg_sender().unwrap()),
        AccessError::NotOwner,
    );
}

Access Control

Smart contracts require the ability to restrict access to and identify certain users or contracts. Unlike account-based blockchains, transactions in UTXO-based blockchains (i.e. Fuel) do not necessarily have a unique transaction sender. Additional logic is needed to handle this difference, and is provided by the standard library.

msg_sender

To deliver an experience akin to the EVM's access control, the std library provides a msg_sender function, which identifies a unique caller based upon the call and/or transaction input data.

```sway\ncontract;

abi MyOwnedContract {
    fn receive(field_1: u64) -> bool;
}

const OWNER = Address::from(0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c);

impl MyOwnedContract for Contract {
    fn receive(field_1: u64) -> bool {
        let sender = msg_sender().unwrap();
        if let Identity::Address(addr) = sender {
            assert(addr == OWNER);
        } else {
            revert(0);
        }

        true
    }
}\n```

The msg_sender function works as follows:

  • If the caller is a contract, then Ok(Sender) is returned with the ContractId sender variant.
  • If the caller is external (i.e. from a script), then all coin input owners in the transaction are checked. If all owners are the same, then Ok(Sender) is returned with the Address sender variant.
  • If the caller is external and coin input owners are different, then the caller cannot be determined and a Err(AuthError) is returned.

Contract Ownership

Many contracts require some form of ownership for access control. The SRC-5 Ownership Standard has been defined to provide an interoperable interface for ownership within contracts.

To accomplish this, use the Ownership Library to keep track of the owner. This allows setting and revoking ownership using the variants Some(..) and None respectively. This is better, safer, and more readable than using the Identity type directly where revoking ownership has to be done using some magic value such as b256::zero() or otherwise.

  • The following is an example of how to properly lock a function such that only the owner may call a function:
```sway\ncontract;

// SRC-5 Ownership Standard `State` enum
pub enum State {
    Uninitialized: (),
    Initialized: Identity,
    Revoked: (),
}

// SRC-5 Ownership Standard `Ownership` struct
pub struct Ownership {
    state: State,
}

// Skeleton implementation of the Ownership Library.
// The library can be found here https://github.com/FuelLabs/sway-libs/tree/master/libs/ownership
impl StorageKey<Ownership> {
    fn renounce_ownership(self) {}
    fn set_ownership(self, identity: Identity) {}
    fn owner(self) -> State {
        State::Uninitialized
    }
    fn only_owner(self) {}
}

impl Ownership {
    fn initialized(identity: Identity) -> Self {
        Self {
            state: State::Initialized(identity),
        }
    }
}

abi OwnershipExample {
    #[storage(write)]
    fn revoke_ownership();
    #[storage(write)]
    fn set_owner(identity: Identity);
    #[storage(read)]
    fn owner() -> State;
    #[storage(read)]
    fn only_owner();
}

// ANCHOR: set_owner_example_storage
storage {
    owner: Ownership = Ownership::initialized(Identity::Address(Address::zero())),
}
// ANCHOR_END: set_owner_example_storage

impl OwnershipExample for Contract {
    // ANCHOR: revoke_owner_example
    #[storage(write)]
    fn revoke_ownership() {
        storage.owner.renounce_ownership();
    }
    // ANCHOR_END: revoke_owner_example
    // ANCHOR: set_owner_example_function
    #[storage(write)]
    fn set_owner(identity: Identity) {
        storage.owner.set_ownership(identity);
    }
    // ANCHOR_END: set_owner_example_function
    // ANCHOR: get_owner_example
    #[storage(read)]
    fn owner() -> State {
        storage.owner.owner()
    }
    // ANCHOR_END: get_owner_example
    // ANCHOR: only_owner_example
    #[storage(read)]
    fn only_owner() {
        storage.owner.only_owner();
        // Do stuff here
    }
    // ANCHOR_END: only_owner_example
}\n```

Setting ownership can be done in one of two ways; During compile time or run time.

  • The following is an example of how to properly set ownership of a contract during compile time:
```sway\ncontract;

// SRC-5 Ownership Standard `State` enum
pub enum State {
    Uninitialized: (),
    Initialized: Identity,
    Revoked: (),
}

// SRC-5 Ownership Standard `Ownership` struct
pub struct Ownership {
    state: State,
}

// Skeleton implementation of the Ownership Library.
// The library can be found here https://github.com/FuelLabs/sway-libs/tree/master/libs/ownership
impl StorageKey<Ownership> {
    fn renounce_ownership(self) {}
    fn set_ownership(self, identity: Identity) {}
    fn owner(self) -> State {
        State::Uninitialized
    }
    fn only_owner(self) {}
}

impl Ownership {
    fn initialized(identity: Identity) -> Self {
        Self {
            state: State::Initialized(identity),
        }
    }
}

abi OwnershipExample {
    #[storage(write)]
    fn revoke_ownership();
    #[storage(write)]
    fn set_owner(identity: Identity);
    #[storage(read)]
    fn owner() -> State;
    #[storage(read)]
    fn only_owner();
}

// ANCHOR: set_owner_example_storage
storage {
    owner: Ownership = Ownership::initialized(Identity::Address(Address::zero())),
}
// ANCHOR_END: set_owner_example_storage

impl OwnershipExample for Contract {
    // ANCHOR: revoke_owner_example
    #[storage(write)]
    fn revoke_ownership() {
        storage.owner.renounce_ownership();
    }
    // ANCHOR_END: revoke_owner_example
    // ANCHOR: set_owner_example_function
    #[storage(write)]
    fn set_owner(identity: Identity) {
        storage.owner.set_ownership(identity);
    }
    // ANCHOR_END: set_owner_example_function
    // ANCHOR: get_owner_example
    #[storage(read)]
    fn owner() -> State {
        storage.owner.owner()
    }
    // ANCHOR_END: get_owner_example
    // ANCHOR: only_owner_example
    #[storage(read)]
    fn only_owner() {
        storage.owner.only_owner();
        // Do stuff here
    }
    // ANCHOR_END: only_owner_example
}\n```
  • The following is an example of how to properly set ownership of a contract during run time:
```sway\ncontract;

// SRC-5 Ownership Standard `State` enum
pub enum State {
    Uninitialized: (),
    Initialized: Identity,
    Revoked: (),
}

// SRC-5 Ownership Standard `Ownership` struct
pub struct Ownership {
    state: State,
}

// Skeleton implementation of the Ownership Library.
// The library can be found here https://github.com/FuelLabs/sway-libs/tree/master/libs/ownership
impl StorageKey<Ownership> {
    fn renounce_ownership(self) {}
    fn set_ownership(self, identity: Identity) {}
    fn owner(self) -> State {
        State::Uninitialized
    }
    fn only_owner(self) {}
}

impl Ownership {
    fn initialized(identity: Identity) -> Self {
        Self {
            state: State::Initialized(identity),
        }
    }
}

abi OwnershipExample {
    #[storage(write)]
    fn revoke_ownership();
    #[storage(write)]
    fn set_owner(identity: Identity);
    #[storage(read)]
    fn owner() -> State;
    #[storage(read)]
    fn only_owner();
}

// ANCHOR: set_owner_example_storage
storage {
    owner: Ownership = Ownership::initialized(Identity::Address(Address::zero())),
}
// ANCHOR_END: set_owner_example_storage

impl OwnershipExample for Contract {
    // ANCHOR: revoke_owner_example
    #[storage(write)]
    fn revoke_ownership() {
        storage.owner.renounce_ownership();
    }
    // ANCHOR_END: revoke_owner_example
    // ANCHOR: set_owner_example_function
    #[storage(write)]
    fn set_owner(identity: Identity) {
        storage.owner.set_ownership(identity);
    }
    // ANCHOR_END: set_owner_example_function
    // ANCHOR: get_owner_example
    #[storage(read)]
    fn owner() -> State {
        storage.owner.owner()
    }
    // ANCHOR_END: get_owner_example
    // ANCHOR: only_owner_example
    #[storage(read)]
    fn only_owner() {
        storage.owner.only_owner();
        // Do stuff here
    }
    // ANCHOR_END: only_owner_example
}\n```
  • The following is an example of how to properly revoke ownership of a contract:
```sway\ncontract;

// SRC-5 Ownership Standard `State` enum
pub enum State {
    Uninitialized: (),
    Initialized: Identity,
    Revoked: (),
}

// SRC-5 Ownership Standard `Ownership` struct
pub struct Ownership {
    state: State,
}

// Skeleton implementation of the Ownership Library.
// The library can be found here https://github.com/FuelLabs/sway-libs/tree/master/libs/ownership
impl StorageKey<Ownership> {
    fn renounce_ownership(self) {}
    fn set_ownership(self, identity: Identity) {}
    fn owner(self) -> State {
        State::Uninitialized
    }
    fn only_owner(self) {}
}

impl Ownership {
    fn initialized(identity: Identity) -> Self {
        Self {
            state: State::Initialized(identity),
        }
    }
}

abi OwnershipExample {
    #[storage(write)]
    fn revoke_ownership();
    #[storage(write)]
    fn set_owner(identity: Identity);
    #[storage(read)]
    fn owner() -> State;
    #[storage(read)]
    fn only_owner();
}

// ANCHOR: set_owner_example_storage
storage {
    owner: Ownership = Ownership::initialized(Identity::Address(Address::zero())),
}
// ANCHOR_END: set_owner_example_storage

impl OwnershipExample for Contract {
    // ANCHOR: revoke_owner_example
    #[storage(write)]
    fn revoke_ownership() {
        storage.owner.renounce_ownership();
    }
    // ANCHOR_END: revoke_owner_example
    // ANCHOR: set_owner_example_function
    #[storage(write)]
    fn set_owner(identity: Identity) {
        storage.owner.set_ownership(identity);
    }
    // ANCHOR_END: set_owner_example_function
    // ANCHOR: get_owner_example
    #[storage(read)]
    fn owner() -> State {
        storage.owner.owner()
    }
    // ANCHOR_END: get_owner_example
    // ANCHOR: only_owner_example
    #[storage(read)]
    fn only_owner() {
        storage.owner.only_owner();
        // Do stuff here
    }
    // ANCHOR_END: only_owner_example
}\n```
  • The following is an example of how to properly retrieve the state of ownership:
```sway\ncontract;

// SRC-5 Ownership Standard `State` enum
pub enum State {
    Uninitialized: (),
    Initialized: Identity,
    Revoked: (),
}

// SRC-5 Ownership Standard `Ownership` struct
pub struct Ownership {
    state: State,
}

// Skeleton implementation of the Ownership Library.
// The library can be found here https://github.com/FuelLabs/sway-libs/tree/master/libs/ownership
impl StorageKey<Ownership> {
    fn renounce_ownership(self) {}
    fn set_ownership(self, identity: Identity) {}
    fn owner(self) -> State {
        State::Uninitialized
    }
    fn only_owner(self) {}
}

impl Ownership {
    fn initialized(identity: Identity) -> Self {
        Self {
            state: State::Initialized(identity),
        }
    }
}

abi OwnershipExample {
    #[storage(write)]
    fn revoke_ownership();
    #[storage(write)]
    fn set_owner(identity: Identity);
    #[storage(read)]
    fn owner() -> State;
    #[storage(read)]
    fn only_owner();
}

// ANCHOR: set_owner_example_storage
storage {
    owner: Ownership = Ownership::initialized(Identity::Address(Address::zero())),
}
// ANCHOR_END: set_owner_example_storage

impl OwnershipExample for Contract {
    // ANCHOR: revoke_owner_example
    #[storage(write)]
    fn revoke_ownership() {
        storage.owner.renounce_ownership();
    }
    // ANCHOR_END: revoke_owner_example
    // ANCHOR: set_owner_example_function
    #[storage(write)]
    fn set_owner(identity: Identity) {
        storage.owner.set_ownership(identity);
    }
    // ANCHOR_END: set_owner_example_function
    // ANCHOR: get_owner_example
    #[storage(read)]
    fn owner() -> State {
        storage.owner.owner()
    }
    // ANCHOR_END: get_owner_example
    // ANCHOR: only_owner_example
    #[storage(read)]
    fn only_owner() {
        storage.owner.only_owner();
        // Do stuff here
    }
    // ANCHOR_END: only_owner_example
}\n```

Access Control Libraries

Sway-Libs provides the following libraries to enable further access control.

Calling Contracts

Smart contracts can be called by other contracts or scripts. In the FuelVM, this is done primarily with the call instruction.

Sway provides a nice way to manage callable interfaces with its ABI system. The Fuel ABI specification can be found here.

Example

Here is an example of a contract calling another contract in Sway. A script can call a contract in the same way.

// ./contract_a.sw
contract;

abi ContractA {
    fn receive(field_1: bool, field_2: u64) -> u64;
}

impl ContractA for Contract {
    fn receive(field_1: bool, field_2: u64) -> u64 {
        assert(field_1 == true);
        assert(field_2 > 0);
        return_45()
    }
}

fn return_45() -> u64 {
  45
}
// ./contract_b.sw
contract;

use contract_a::ContractA;

abi ContractB {
    fn make_call();
}

const contract_id = 0x79fa8779bed2f36c3581d01c79df8da45eee09fac1fd76a5a656e16326317ef0;

impl ContractB for Contract {
    fn make_call() {
      let x = abi(ContractA, contract_id);
      let return_value = x.receive(true, 3); // will be 45
    }
}

Note: The ABI is for external calls only therefore you cannot define a method in the ABI and call it in the same contract. If you want to define a function for a contract, but keep it private so that only your contract can call it, you can define it outside of the impl and call it inside the contract, similar to the return_45() function above.

Advanced Calls

All calls forward a gas stipend, and may additionally forward one native asset with the call.

Here is an example of how to specify the amount of gas (gas), the asset ID of the native asset (asset_id), and the amount of the native asset (coins) to forward:

script;

abi MyContract {
    fn foo(field_1: bool, field_2: u64);
}

fn main() {
    let x = abi(MyContract, 0x79fa8779bed2f36c3581d01c79df8da45eee09fac1fd76a5a656e16326317ef0);
    let asset_id = 0x7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777;
    x.foo {
        gas: 5000, asset_id: asset_id, coins: 5000
    }
    (true, 3);
}

Handling Re-entrancy

A common attack vector for smart contracts is re-entrancy. Similar to the EVM, the FuelVM allows for re-entrancy.

A stateless re-entrancy guard is included in the sway-libs library. The guard will panic (revert) at run time if re-entrancy is detected.

contract;

use reentrancy::reentrancy_guard;

abi MyContract {
    fn some_method();
}

impl ContractB for Contract {
    fn some_method() {
        reentrancy_guard();
        // do something
    }
}

CEI pattern violation static analysis

Another way of avoiding re-entrancy-related attacks is to follow the so-called CEI pattern. CEI stands for "Checks, Effects, Interactions", meaning that the contract code should first perform safety checks, also known as "pre-conditions", then perform effects, i.e. modify or read the contract storage and execute external contract calls (interaction) only at the very end of the function/method.

Please see this blog post for more detail on some vulnerabilities in case of storage modification after interaction and this blog post for more information on storage reads after interaction.

The Sway compiler implements a check that the CEI pattern is not violated in the user contract and issues warnings if that's the case.

For example, in the following contract the CEI pattern is violated, because an external contract call is executed before a storage write.

```sway\ncontract;

mod other_contract;

use other_contract::*;
use std::hash::*;

abi MyContract {
    #[storage(read, write)]
    fn withdraw(external_contract_id: ContractId);
}

storage {
    balances: StorageMap<Identity, u64> = StorageMap::<Identity, u64> {},
}

impl MyContract for Contract {
    #[storage(read, write)]
    fn withdraw(external_contract_id: ContractId) {
        let sender = msg_sender().unwrap();
        let bal = storage.balances.get(sender).try_read().unwrap_or(0);

        assert(bal > 0);

        // External call
        let caller = abi(OtherContract, external_contract_id.into());
        caller.external_call {
            coins: bal,
        }();

        // Storage update _after_ external call
        storage.balances.insert(sender, 0);
    }
}\n```

Here, other_contract is defined as follows:

```sway\nlibrary;

abi OtherContract {
    #[payable]
    fn external_call();
}\n```

The CEI pattern analyzer issues a warning as follows, pointing to the interaction before a storage modification:

warning
  --> /path/to/contract/main.sw:28:9
   |
26 |
27 |           let caller = abi(OtherContract, external_contract_id.into());
28 |           caller.external_call { coins: bal }();
   |  _________-
29 | |
30 | |         // Storage update _after_ external call
31 | |         storage.balances.insert(sender, 0);
   | |__________________________________________- Storage write after external contract interaction in function or method "withdraw". Consider making all storage writes before calling another contract
32 |       }
33 |   }
   |
____

In case there is a storage read after an interaction, the CEI analyzer will issue a similar warning.

In addition to storage reads and writes after an interaction, the CEI analyzer reports analogous warnings about:

  • balance tree updates, i.e. balance tree reads with subsequent writes, which may be produced by the tr and tro ASM instructions or library functions using them under the hood;
  • balance trees reads with bal instruction;
  • changes to the output messages that can be produced by the __smo intrinsic function or the smo ASM instruction.

Differences from the EVM

While the Fuel contract calling paradigm is similar to the EVM's (using an ABI, forwarding gas and data), it differs in two key ways:

  1. Native assets: FuelVM calls can forward any native asset not just base asset.

  2. No data serialization: Contract calls in the FuelVM do not need to serialize data to pass it between contracts; instead they simply pass a pointer to the data. This is because the FuelVM has a shared global memory which all call frames can read from.

Fallback

When a contract is compiled, a special section called "contract selection" is also generated. This section checks if the contract call method matches any of the available ABI methods. If this fails, one of two possible actions will happen:

1 - if no fallback function was specified, the contract will revert; 2 - otherwise, the fallback function will be called.

For all intents and purposes the fallback function is considered a contract method, which means that it has all the limitations that other contract methods have. As the fallback function signature, the function cannot have arguments, but they can return anything.

If for some reason the fallback function needs to returns different types, the intrinsic __contract_ret can be used.

contract;

abi MyContract {
    fn some_method();
}

impl ContractB for Contract {
    fn some_method() {
    }
}

#[fallback]
fn fallback() {
}

You may still access the method selector and arguments to the call in the fallback. For instance, let's assume a function fn foobar(bool, u64) {} gets called on a contract that doesn't have it with arguments true and 42. It can execute the following fallback:

#[fallback]
fn fallback() {
    // the method selector is the first four bytes of sha256("foobar(bool,u64)")
    // per https://fuellabs.github.io/fuel-specs/master/protocol/abi#function-selector-encoding
    let method_selector = std::call_frames::first_param::<u64>();

    // the arguments tuple is (true, 42)
    let arguments = std::call_frames::second_param::<(bool, u64)>();
}

External Code Execution

The std-lib includes a function called run_external that allows Sway contracts to execute arbitrary external Sway code.

This functionality enables features like upgradeable contracts and proxies.

Upgradeable Contracts

Upgradeable contracts are designed to allow the logic of a smart contract to be updated after deployment.

Consider this example proxy contract:

```sway\ncontract;

use std::execution::run_external;

abi Proxy {
    #[storage(write)]
    fn set_target_contract(id: ContractId);

    #[storage(read)]
    fn double_input(_value: u64) -> u64;
}

// ANCHOR: proxy
#[namespace(my_storage_namespace)]
storage {
    target_contract: Option<ContractId> = None,
}

impl Proxy for Contract {
    #[storage(write)]
    fn set_target_contract(id: ContractId) {
        storage.target_contract.write(Some(id));
    }

    #[storage(read)]
    fn double_input(_value: u64) -> u64 {
        let target = storage.target_contract.read().unwrap();
        run_external(target)
    }
}
// ANCHOR_END: proxy\n```

The contract has two functions:

  • set_target_contract updates the target_contract variable in storage with the ContractId of an external contract.
  • double_input reads the target_contract from storage and uses it to run external code. If the target_contract has a function with the same name (double_input), the code in the external double_input function will run. In this case, the function will return a u64.

Notice in the Proxy example above, the storage block has a namespace attribute. Using this attribute is considered a best practice for all proxy contracts in Sway, because it will prevent storage collisions with the implementation contract, as the implementation contract has access to both storage contexts.

Below is what an implementation contract could look like for this:

```sway\ncontract;

abi Implementation {
    #[storage(write)]
    fn double_input(value: u64) -> u64;
}

// ANCHOR: target
storage {
    value: u64 = 0,
    // to stay compatible, this has to stay the same in the next version
}

impl Implementation for Contract {
    #[storage(write)]
    fn double_input(value: u64) -> u64 {
        let new_value = value * 2;
        storage.value.write(new_value);
        new_value
    }
}
// ANCHOR_END: target\n```

This contract has one function called double_input, which calculates the input value times two, updates the value variable in storage, and returns the new value.

How does this differ from calling a contract?

There are a couple of major differences between calling a contract directly and using the run_external method.

First, to use run_external, the ABI of the external contract is not required. The proxy contract has no knowledge of the external contract except for its ContractId.

Upgradeable Contract Storage

Second, the storage context of the proxy contract is retained for the loaded code. This means that in the examples above, the value variable gets updated in the storage for the proxy contract.

For example, if you were to read the value variable by directly calling the implementation contract, you would get a different result than if you read it through the proxy contract. The proxy contract loads the code and executes it in its own context.

Fallback functions

If the function name doesn't exist in the target contract but a fallback function does, the fallback function will be triggered.

If there is no fallback function, the transaction will revert.

You can access function parameters for fallback functions using the call_frames module in the std-lib. For example, to access the _foo input parameter in the proxy function below, you can use the called_args method in the fallback function:

```sway\ncontract;

use std::execution::run_external;

configurable {
    TARGET: ContractId = ContractId::zero(),
}

abi RunExternalTest {
    fn double_value(foo: u64) -> u64;
    fn large_value() -> b256;
    fn does_not_exist_in_the_target(foo: u64) -> u64;
}
impl RunExternalTest for Contract {
    fn double_value(_foo: u64) -> u64 {
        __log(1);
        run_external(TARGET)
    }

    fn large_value() -> b256 {
        run_external(TARGET)
    }

    // ANCHOR: does_not_exist_in_the_target
    fn does_not_exist_in_the_target(_foo: u64) -> u64 {
        run_external(TARGET)
    }
    // ANCHOR_END: does_not_exist_in_the_target
}\n```
```sway\ncontract;

abi RunExternalTest {
    fn double_value(foo: u64) -> u64;
    fn large_value() -> b256;
}

impl RunExternalTest for Contract {
    fn double_value(foo: u64) -> u64 {
        __log(2);
        foo * 2
    }
    fn large_value() -> b256 {
       0x00000000000000000000000059F2f1fCfE2474fD5F0b9BA1E73ca90b143Eb8d0
    }
}

// ANCHOR: fallback
#[fallback]
fn fallback() -> u64 {
    use std::call_frames::*;
    __log(3);
    __log(called_method());
    __log("double_value");
    __log(called_method() == "double_value");
    let foo = called_args::<u64>();
    foo * 3
}
// ANCHOR_END: fallback\n```

In this case, the does_not_exist_in_the_target function will return _foo * 3.

Limitations

Some limitations of run_external function are:

  • It can only be used with other contracts. Scripts, predicates, and library code cannot be run externally.
  • If you change the implementation contract, you must maintain the same order of previous storage variables and types, as this is what has been stored in the proxy storage.
  • You can't use the call stack in another call frame before you use run_external. You can only use the call stack within the call frame that contains run_external.

Advanced Concepts

Advanced concepts.

Advanced Types

Creating Type Synonyms with Type Aliases

Sway provides the ability to declare a type alias to give an existing type another name. For this we use the type keyword. For example, we can create the alias Kilometers to u64 like so:

```sway\nscript;

// ANCHOR: type_alias 
type Kilometers = u64;
// ANCHOR_END: type_alias 

struct MyStruct<T, U> {
    x: T,
    y: U,
}
// ANCHOR: long_type_use
fn foo_long(array: [MyStruct<u64, b256>; 5]) -> [MyStruct<u64, b256>; 5] {
    array
}
// ANCHOR_END: long_type_use

// ANCHOR: long_type_use_shorter
type MyArray = [MyStruct<u64, b256>; 5];

fn foo_shorter(array: MyArray) -> MyArray {
    array
}
// ANCHOR_END: long_type_use_shorter

fn main() {
    // ANCHOR: addition 
    let x: u64 = 5;
    let y: Kilometers = 5;
    assert(x + y == 10);
    // ANCHOR_END: addition 
}\n```

Now, the alias Kilometers is a synonym for u64. Note that Kilometers is not a separate new type. Values that have the type Kilometers will be treated the same as values of type u64:

```sway\nscript;

// ANCHOR: type_alias 
type Kilometers = u64;
// ANCHOR_END: type_alias 

struct MyStruct<T, U> {
    x: T,
    y: U,
}
// ANCHOR: long_type_use
fn foo_long(array: [MyStruct<u64, b256>; 5]) -> [MyStruct<u64, b256>; 5] {
    array
}
// ANCHOR_END: long_type_use

// ANCHOR: long_type_use_shorter
type MyArray = [MyStruct<u64, b256>; 5];

fn foo_shorter(array: MyArray) -> MyArray {
    array
}
// ANCHOR_END: long_type_use_shorter

fn main() {
    // ANCHOR: addition 
    let x: u64 = 5;
    let y: Kilometers = 5;
    assert(x + y == 10);
    // ANCHOR_END: addition 
}\n```

Because Kilometers and u64 are the same type, we can add values of both types and we can pass Kilometers values to functions that take u64 parameters. However, using this method, we don’t get the type checking benefits that we get from introducing a separate new type called Kilometers. In other words, if we mix up Kilometers and i32 values somewhere, the compiler will not give us an error.

The main use case for type synonyms is to reduce repetition. For example, we might have a lengthy array type like this:

[MyStruct<u64, b256>; 5]

Writing this lengthy type in function signatures and as type annotations all over the code can be tiresome and error prone. Imagine having a project full of code like this:

```sway\nscript;

// ANCHOR: type_alias 
type Kilometers = u64;
// ANCHOR_END: type_alias 

struct MyStruct<T, U> {
    x: T,
    y: U,
}
// ANCHOR: long_type_use
fn foo_long(array: [MyStruct<u64, b256>; 5]) -> [MyStruct<u64, b256>; 5] {
    array
}
// ANCHOR_END: long_type_use

// ANCHOR: long_type_use_shorter
type MyArray = [MyStruct<u64, b256>; 5];

fn foo_shorter(array: MyArray) -> MyArray {
    array
}
// ANCHOR_END: long_type_use_shorter

fn main() {
    // ANCHOR: addition 
    let x: u64 = 5;
    let y: Kilometers = 5;
    assert(x + y == 10);
    // ANCHOR_END: addition 
}\n```

A type alias makes this code more manageable by reducing the repetition. Below, we’ve introduced an alias named MyArray for the verbose type and can replace all uses of the type with the shorter alias MyArray:

```sway\nscript;

// ANCHOR: type_alias 
type Kilometers = u64;
// ANCHOR_END: type_alias 

struct MyStruct<T, U> {
    x: T,
    y: U,
}
// ANCHOR: long_type_use
fn foo_long(array: [MyStruct<u64, b256>; 5]) -> [MyStruct<u64, b256>; 5] {
    array
}
// ANCHOR_END: long_type_use

// ANCHOR: long_type_use_shorter
type MyArray = [MyStruct<u64, b256>; 5];

fn foo_shorter(array: MyArray) -> MyArray {
    array
}
// ANCHOR_END: long_type_use_shorter

fn main() {
    // ANCHOR: addition 
    let x: u64 = 5;
    let y: Kilometers = 5;
    assert(x + y == 10);
    // ANCHOR_END: addition 
}\n```

This code is much easier to read and write! Choosing a meaningful name for a type alias can help communicate your intent as well.

Advanced Storage

Nested Storage Collections

Through the use of StorageKeys, you may have nested storage collections such as storing a StorageString in a StorageMap<K, V>.

For example, here we have a few common nested storage types declared in a storage block:

```sway\ncontract;

use std::{
    bytes::Bytes,
    hash::{
        Hash,
        sha256,
    },
    storage::{
        storage_bytes::*,
        storage_string::*,
        storage_vec::*,
    },
    string::String,
};

// ANCHOR: nested_storage_declaration
storage {
    nested_map_vec: StorageMap<u64, StorageVec<u8>> = StorageMap {},
    nested_map_string: StorageMap<u64, StorageString> = StorageMap {},
    nested_vec_bytes: StorageVec<StorageBytes> = StorageVec {},
}
// ANCHOR_END: nested_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map_vec();
    #[storage(read, write)]
    fn get_map_vec();
    #[storage(write)]
    fn store_map_string();
    #[storage(read)]
    fn get_map_string();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup and initialize storage for the StorageVec.
        storage.nested_map_vec.try_insert(10, StorageVec {});

        // Method 1: Push to the vec directly
        storage.nested_map_vec.get(10).push(1u8);
        storage.nested_map_vec.get(10).push(2u8);
        storage.nested_map_vec.get(10).push(3u8);

        // Method 2: First get the storage key and then push the values.
        let storage_key_vec: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        storage_key_vec.push(4u8);
        storage_key_vec.push(5u8);
        storage_key_vec.push(6u8);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_map_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the StorageVec directly.
        let stored_val1: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val2: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val3: u8 = storage.nested_map_vec.get(10).pop().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        let stored_val4: u8 = storage_key.pop().unwrap();
        let stored_val5: u8 = storage_key.pop().unwrap();
        let stored_val6: u8 = storage_key.pop().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }

    #[storage(write)]
    fn store_map_string() {
        // ANCHOR: nested_string_storage_write
        // Setup and initialize storage for the StorageString.
        storage.nested_map_string.try_insert(10, StorageString {});

        // Method 1: Store the string directly.
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.nested_map_string.get(10).write_slice(my_string);

        // Method 2: First get the storage key and then write the value.
        let my_string = String::from_ascii_str("Fuel is modular");
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        storage_key.write_slice(my_string);
        // ANCHOR_END: nested_string_storage_write
    }
    #[storage(read)]
    fn get_map_string() {
        // ANCHOR: nested_string_storage_read
        // Method 1: Access the string directly.
        let stored_string: String = storage.nested_map_string.get(10).read_slice().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        let stored_string: String = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_string_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup Bytes to store
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Setup and initialize storage for the StorageBytes.
        storage.nested_vec_bytes.push(StorageBytes {});

        // Method 1: Store the bytes by accessing StorageBytes directly.
        storage
            .nested_vec_bytes
            .get(0)
            .unwrap()
            .write_slice(my_bytes);

        // Method 2: First get the storage key and then write the bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        storage_key.write_slice(my_bytes);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the stored bytes directly.
        let stored_bytes: Bytes = storage.nested_vec_bytes.get(0).unwrap().read_slice().unwrap();

        // Method 2: First get the storage key and then access the stored bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        let stored_bytes: Bytes = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }
}\n```

Please note that storage initialization is needed to do this.

NOTE: When importing a storage type, please be sure to use the glob operator i.e. use std::storage::storage_vec::*.

Storing a StorageVec<T> in a StorageMap<K, V>

The following demonstrates how to write to a StorageVec<T> that is nested in a StorageMap<T, V>:

```sway\ncontract;

use std::{
    bytes::Bytes,
    hash::{
        Hash,
        sha256,
    },
    storage::{
        storage_bytes::*,
        storage_string::*,
        storage_vec::*,
    },
    string::String,
};

// ANCHOR: nested_storage_declaration
storage {
    nested_map_vec: StorageMap<u64, StorageVec<u8>> = StorageMap {},
    nested_map_string: StorageMap<u64, StorageString> = StorageMap {},
    nested_vec_bytes: StorageVec<StorageBytes> = StorageVec {},
}
// ANCHOR_END: nested_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map_vec();
    #[storage(read, write)]
    fn get_map_vec();
    #[storage(write)]
    fn store_map_string();
    #[storage(read)]
    fn get_map_string();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup and initialize storage for the StorageVec.
        storage.nested_map_vec.try_insert(10, StorageVec {});

        // Method 1: Push to the vec directly
        storage.nested_map_vec.get(10).push(1u8);
        storage.nested_map_vec.get(10).push(2u8);
        storage.nested_map_vec.get(10).push(3u8);

        // Method 2: First get the storage key and then push the values.
        let storage_key_vec: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        storage_key_vec.push(4u8);
        storage_key_vec.push(5u8);
        storage_key_vec.push(6u8);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_map_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the StorageVec directly.
        let stored_val1: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val2: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val3: u8 = storage.nested_map_vec.get(10).pop().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        let stored_val4: u8 = storage_key.pop().unwrap();
        let stored_val5: u8 = storage_key.pop().unwrap();
        let stored_val6: u8 = storage_key.pop().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }

    #[storage(write)]
    fn store_map_string() {
        // ANCHOR: nested_string_storage_write
        // Setup and initialize storage for the StorageString.
        storage.nested_map_string.try_insert(10, StorageString {});

        // Method 1: Store the string directly.
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.nested_map_string.get(10).write_slice(my_string);

        // Method 2: First get the storage key and then write the value.
        let my_string = String::from_ascii_str("Fuel is modular");
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        storage_key.write_slice(my_string);
        // ANCHOR_END: nested_string_storage_write
    }
    #[storage(read)]
    fn get_map_string() {
        // ANCHOR: nested_string_storage_read
        // Method 1: Access the string directly.
        let stored_string: String = storage.nested_map_string.get(10).read_slice().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        let stored_string: String = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_string_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup Bytes to store
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Setup and initialize storage for the StorageBytes.
        storage.nested_vec_bytes.push(StorageBytes {});

        // Method 1: Store the bytes by accessing StorageBytes directly.
        storage
            .nested_vec_bytes
            .get(0)
            .unwrap()
            .write_slice(my_bytes);

        // Method 2: First get the storage key and then write the bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        storage_key.write_slice(my_bytes);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the stored bytes directly.
        let stored_bytes: Bytes = storage.nested_vec_bytes.get(0).unwrap().read_slice().unwrap();

        // Method 2: First get the storage key and then access the stored bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        let stored_bytes: Bytes = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }
}\n```

The following demonstrates how to read from a StorageVec<T> that is nested in a StorageMap<T, V>:

```sway\ncontract;

use std::{
    bytes::Bytes,
    hash::{
        Hash,
        sha256,
    },
    storage::{
        storage_bytes::*,
        storage_string::*,
        storage_vec::*,
    },
    string::String,
};

// ANCHOR: nested_storage_declaration
storage {
    nested_map_vec: StorageMap<u64, StorageVec<u8>> = StorageMap {},
    nested_map_string: StorageMap<u64, StorageString> = StorageMap {},
    nested_vec_bytes: StorageVec<StorageBytes> = StorageVec {},
}
// ANCHOR_END: nested_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map_vec();
    #[storage(read, write)]
    fn get_map_vec();
    #[storage(write)]
    fn store_map_string();
    #[storage(read)]
    fn get_map_string();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup and initialize storage for the StorageVec.
        storage.nested_map_vec.try_insert(10, StorageVec {});

        // Method 1: Push to the vec directly
        storage.nested_map_vec.get(10).push(1u8);
        storage.nested_map_vec.get(10).push(2u8);
        storage.nested_map_vec.get(10).push(3u8);

        // Method 2: First get the storage key and then push the values.
        let storage_key_vec: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        storage_key_vec.push(4u8);
        storage_key_vec.push(5u8);
        storage_key_vec.push(6u8);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_map_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the StorageVec directly.
        let stored_val1: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val2: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val3: u8 = storage.nested_map_vec.get(10).pop().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        let stored_val4: u8 = storage_key.pop().unwrap();
        let stored_val5: u8 = storage_key.pop().unwrap();
        let stored_val6: u8 = storage_key.pop().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }

    #[storage(write)]
    fn store_map_string() {
        // ANCHOR: nested_string_storage_write
        // Setup and initialize storage for the StorageString.
        storage.nested_map_string.try_insert(10, StorageString {});

        // Method 1: Store the string directly.
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.nested_map_string.get(10).write_slice(my_string);

        // Method 2: First get the storage key and then write the value.
        let my_string = String::from_ascii_str("Fuel is modular");
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        storage_key.write_slice(my_string);
        // ANCHOR_END: nested_string_storage_write
    }
    #[storage(read)]
    fn get_map_string() {
        // ANCHOR: nested_string_storage_read
        // Method 1: Access the string directly.
        let stored_string: String = storage.nested_map_string.get(10).read_slice().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        let stored_string: String = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_string_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup Bytes to store
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Setup and initialize storage for the StorageBytes.
        storage.nested_vec_bytes.push(StorageBytes {});

        // Method 1: Store the bytes by accessing StorageBytes directly.
        storage
            .nested_vec_bytes
            .get(0)
            .unwrap()
            .write_slice(my_bytes);

        // Method 2: First get the storage key and then write the bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        storage_key.write_slice(my_bytes);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the stored bytes directly.
        let stored_bytes: Bytes = storage.nested_vec_bytes.get(0).unwrap().read_slice().unwrap();

        // Method 2: First get the storage key and then access the stored bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        let stored_bytes: Bytes = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }
}\n```

Storing a StorageString in a StorageMap<K, V>

The following demonstrates how to write to a StorageString that is nested in a StorageMap<T, V>:

```sway\ncontract;

use std::{
    bytes::Bytes,
    hash::{
        Hash,
        sha256,
    },
    storage::{
        storage_bytes::*,
        storage_string::*,
        storage_vec::*,
    },
    string::String,
};

// ANCHOR: nested_storage_declaration
storage {
    nested_map_vec: StorageMap<u64, StorageVec<u8>> = StorageMap {},
    nested_map_string: StorageMap<u64, StorageString> = StorageMap {},
    nested_vec_bytes: StorageVec<StorageBytes> = StorageVec {},
}
// ANCHOR_END: nested_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map_vec();
    #[storage(read, write)]
    fn get_map_vec();
    #[storage(write)]
    fn store_map_string();
    #[storage(read)]
    fn get_map_string();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup and initialize storage for the StorageVec.
        storage.nested_map_vec.try_insert(10, StorageVec {});

        // Method 1: Push to the vec directly
        storage.nested_map_vec.get(10).push(1u8);
        storage.nested_map_vec.get(10).push(2u8);
        storage.nested_map_vec.get(10).push(3u8);

        // Method 2: First get the storage key and then push the values.
        let storage_key_vec: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        storage_key_vec.push(4u8);
        storage_key_vec.push(5u8);
        storage_key_vec.push(6u8);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_map_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the StorageVec directly.
        let stored_val1: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val2: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val3: u8 = storage.nested_map_vec.get(10).pop().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        let stored_val4: u8 = storage_key.pop().unwrap();
        let stored_val5: u8 = storage_key.pop().unwrap();
        let stored_val6: u8 = storage_key.pop().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }

    #[storage(write)]
    fn store_map_string() {
        // ANCHOR: nested_string_storage_write
        // Setup and initialize storage for the StorageString.
        storage.nested_map_string.try_insert(10, StorageString {});

        // Method 1: Store the string directly.
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.nested_map_string.get(10).write_slice(my_string);

        // Method 2: First get the storage key and then write the value.
        let my_string = String::from_ascii_str("Fuel is modular");
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        storage_key.write_slice(my_string);
        // ANCHOR_END: nested_string_storage_write
    }
    #[storage(read)]
    fn get_map_string() {
        // ANCHOR: nested_string_storage_read
        // Method 1: Access the string directly.
        let stored_string: String = storage.nested_map_string.get(10).read_slice().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        let stored_string: String = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_string_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup Bytes to store
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Setup and initialize storage for the StorageBytes.
        storage.nested_vec_bytes.push(StorageBytes {});

        // Method 1: Store the bytes by accessing StorageBytes directly.
        storage
            .nested_vec_bytes
            .get(0)
            .unwrap()
            .write_slice(my_bytes);

        // Method 2: First get the storage key and then write the bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        storage_key.write_slice(my_bytes);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the stored bytes directly.
        let stored_bytes: Bytes = storage.nested_vec_bytes.get(0).unwrap().read_slice().unwrap();

        // Method 2: First get the storage key and then access the stored bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        let stored_bytes: Bytes = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }
}\n```

The following demonstrates how to read from a StorageString that is nested in a StorageMap<T, V>:

```sway\ncontract;

use std::{
    bytes::Bytes,
    hash::{
        Hash,
        sha256,
    },
    storage::{
        storage_bytes::*,
        storage_string::*,
        storage_vec::*,
    },
    string::String,
};

// ANCHOR: nested_storage_declaration
storage {
    nested_map_vec: StorageMap<u64, StorageVec<u8>> = StorageMap {},
    nested_map_string: StorageMap<u64, StorageString> = StorageMap {},
    nested_vec_bytes: StorageVec<StorageBytes> = StorageVec {},
}
// ANCHOR_END: nested_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map_vec();
    #[storage(read, write)]
    fn get_map_vec();
    #[storage(write)]
    fn store_map_string();
    #[storage(read)]
    fn get_map_string();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup and initialize storage for the StorageVec.
        storage.nested_map_vec.try_insert(10, StorageVec {});

        // Method 1: Push to the vec directly
        storage.nested_map_vec.get(10).push(1u8);
        storage.nested_map_vec.get(10).push(2u8);
        storage.nested_map_vec.get(10).push(3u8);

        // Method 2: First get the storage key and then push the values.
        let storage_key_vec: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        storage_key_vec.push(4u8);
        storage_key_vec.push(5u8);
        storage_key_vec.push(6u8);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_map_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the StorageVec directly.
        let stored_val1: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val2: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val3: u8 = storage.nested_map_vec.get(10).pop().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        let stored_val4: u8 = storage_key.pop().unwrap();
        let stored_val5: u8 = storage_key.pop().unwrap();
        let stored_val6: u8 = storage_key.pop().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }

    #[storage(write)]
    fn store_map_string() {
        // ANCHOR: nested_string_storage_write
        // Setup and initialize storage for the StorageString.
        storage.nested_map_string.try_insert(10, StorageString {});

        // Method 1: Store the string directly.
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.nested_map_string.get(10).write_slice(my_string);

        // Method 2: First get the storage key and then write the value.
        let my_string = String::from_ascii_str("Fuel is modular");
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        storage_key.write_slice(my_string);
        // ANCHOR_END: nested_string_storage_write
    }
    #[storage(read)]
    fn get_map_string() {
        // ANCHOR: nested_string_storage_read
        // Method 1: Access the string directly.
        let stored_string: String = storage.nested_map_string.get(10).read_slice().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        let stored_string: String = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_string_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup Bytes to store
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Setup and initialize storage for the StorageBytes.
        storage.nested_vec_bytes.push(StorageBytes {});

        // Method 1: Store the bytes by accessing StorageBytes directly.
        storage
            .nested_vec_bytes
            .get(0)
            .unwrap()
            .write_slice(my_bytes);

        // Method 2: First get the storage key and then write the bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        storage_key.write_slice(my_bytes);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the stored bytes directly.
        let stored_bytes: Bytes = storage.nested_vec_bytes.get(0).unwrap().read_slice().unwrap();

        // Method 2: First get the storage key and then access the stored bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        let stored_bytes: Bytes = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }
}\n```

Storing a StorageBytes in a StorageVec<T>

The following demonstrates how to write to a StorageBytes that is nested in a StorageVec<T>:

```sway\ncontract;

use std::{
    bytes::Bytes,
    hash::{
        Hash,
        sha256,
    },
    storage::{
        storage_bytes::*,
        storage_string::*,
        storage_vec::*,
    },
    string::String,
};

// ANCHOR: nested_storage_declaration
storage {
    nested_map_vec: StorageMap<u64, StorageVec<u8>> = StorageMap {},
    nested_map_string: StorageMap<u64, StorageString> = StorageMap {},
    nested_vec_bytes: StorageVec<StorageBytes> = StorageVec {},
}
// ANCHOR_END: nested_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map_vec();
    #[storage(read, write)]
    fn get_map_vec();
    #[storage(write)]
    fn store_map_string();
    #[storage(read)]
    fn get_map_string();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup and initialize storage for the StorageVec.
        storage.nested_map_vec.try_insert(10, StorageVec {});

        // Method 1: Push to the vec directly
        storage.nested_map_vec.get(10).push(1u8);
        storage.nested_map_vec.get(10).push(2u8);
        storage.nested_map_vec.get(10).push(3u8);

        // Method 2: First get the storage key and then push the values.
        let storage_key_vec: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        storage_key_vec.push(4u8);
        storage_key_vec.push(5u8);
        storage_key_vec.push(6u8);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_map_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the StorageVec directly.
        let stored_val1: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val2: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val3: u8 = storage.nested_map_vec.get(10).pop().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        let stored_val4: u8 = storage_key.pop().unwrap();
        let stored_val5: u8 = storage_key.pop().unwrap();
        let stored_val6: u8 = storage_key.pop().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }

    #[storage(write)]
    fn store_map_string() {
        // ANCHOR: nested_string_storage_write
        // Setup and initialize storage for the StorageString.
        storage.nested_map_string.try_insert(10, StorageString {});

        // Method 1: Store the string directly.
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.nested_map_string.get(10).write_slice(my_string);

        // Method 2: First get the storage key and then write the value.
        let my_string = String::from_ascii_str("Fuel is modular");
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        storage_key.write_slice(my_string);
        // ANCHOR_END: nested_string_storage_write
    }
    #[storage(read)]
    fn get_map_string() {
        // ANCHOR: nested_string_storage_read
        // Method 1: Access the string directly.
        let stored_string: String = storage.nested_map_string.get(10).read_slice().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        let stored_string: String = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_string_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup Bytes to store
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Setup and initialize storage for the StorageBytes.
        storage.nested_vec_bytes.push(StorageBytes {});

        // Method 1: Store the bytes by accessing StorageBytes directly.
        storage
            .nested_vec_bytes
            .get(0)
            .unwrap()
            .write_slice(my_bytes);

        // Method 2: First get the storage key and then write the bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        storage_key.write_slice(my_bytes);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the stored bytes directly.
        let stored_bytes: Bytes = storage.nested_vec_bytes.get(0).unwrap().read_slice().unwrap();

        // Method 2: First get the storage key and then access the stored bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        let stored_bytes: Bytes = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }
}\n```

The following demonstrates how to read from a StorageBytes that is nested in a StorageVec<T>:

```sway\ncontract;

use std::{
    bytes::Bytes,
    hash::{
        Hash,
        sha256,
    },
    storage::{
        storage_bytes::*,
        storage_string::*,
        storage_vec::*,
    },
    string::String,
};

// ANCHOR: nested_storage_declaration
storage {
    nested_map_vec: StorageMap<u64, StorageVec<u8>> = StorageMap {},
    nested_map_string: StorageMap<u64, StorageString> = StorageMap {},
    nested_vec_bytes: StorageVec<StorageBytes> = StorageVec {},
}
// ANCHOR_END: nested_storage_declaration

abi StorageExample {
    #[storage(write)]
    fn store_map_vec();
    #[storage(read, write)]
    fn get_map_vec();
    #[storage(write)]
    fn store_map_string();
    #[storage(read)]
    fn get_map_string();
    #[storage(write)]
    fn store_vec();
    #[storage(read, write)]
    fn get_vec();
}

impl StorageExample for Contract {
    #[storage(write)]
    fn store_map_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup and initialize storage for the StorageVec.
        storage.nested_map_vec.try_insert(10, StorageVec {});

        // Method 1: Push to the vec directly
        storage.nested_map_vec.get(10).push(1u8);
        storage.nested_map_vec.get(10).push(2u8);
        storage.nested_map_vec.get(10).push(3u8);

        // Method 2: First get the storage key and then push the values.
        let storage_key_vec: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        storage_key_vec.push(4u8);
        storage_key_vec.push(5u8);
        storage_key_vec.push(6u8);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_map_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the StorageVec directly.
        let stored_val1: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val2: u8 = storage.nested_map_vec.get(10).pop().unwrap();
        let stored_val3: u8 = storage.nested_map_vec.get(10).pop().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageVec<u8>> = storage.nested_map_vec.get(10);
        let stored_val4: u8 = storage_key.pop().unwrap();
        let stored_val5: u8 = storage_key.pop().unwrap();
        let stored_val6: u8 = storage_key.pop().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }

    #[storage(write)]
    fn store_map_string() {
        // ANCHOR: nested_string_storage_write
        // Setup and initialize storage for the StorageString.
        storage.nested_map_string.try_insert(10, StorageString {});

        // Method 1: Store the string directly.
        let my_string = String::from_ascii_str("Fuel is blazingly fast");
        storage.nested_map_string.get(10).write_slice(my_string);

        // Method 2: First get the storage key and then write the value.
        let my_string = String::from_ascii_str("Fuel is modular");
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        storage_key.write_slice(my_string);
        // ANCHOR_END: nested_string_storage_write
    }
    #[storage(read)]
    fn get_map_string() {
        // ANCHOR: nested_string_storage_read
        // Method 1: Access the string directly.
        let stored_string: String = storage.nested_map_string.get(10).read_slice().unwrap();

        // Method 2: First get the storage key and then access the value.
        let storage_key: StorageKey<StorageString> = storage.nested_map_string.get(10);
        let stored_string: String = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_string_storage_read
    }

    #[storage(write)]
    fn store_vec() {
        // ANCHOR: nested_vec_storage_write
        // Setup Bytes to store
        let mut my_bytes = Bytes::new();
        my_bytes.push(1u8);
        my_bytes.push(2u8);
        my_bytes.push(3u8);

        // Setup and initialize storage for the StorageBytes.
        storage.nested_vec_bytes.push(StorageBytes {});

        // Method 1: Store the bytes by accessing StorageBytes directly.
        storage
            .nested_vec_bytes
            .get(0)
            .unwrap()
            .write_slice(my_bytes);

        // Method 2: First get the storage key and then write the bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        storage_key.write_slice(my_bytes);
        // ANCHOR_END: nested_vec_storage_write
    }
    #[storage(read, write)]
    fn get_vec() {
        // ANCHOR: nested_vec_storage_read
        // Method 1: Access the stored bytes directly.
        let stored_bytes: Bytes = storage.nested_vec_bytes.get(0).unwrap().read_slice().unwrap();

        // Method 2: First get the storage key and then access the stored bytes.
        let storage_key: StorageKey<StorageBytes> = storage.nested_vec_bytes.get(0).unwrap();
        let stored_bytes: Bytes = storage_key.read_slice().unwrap();
        // ANCHOR_END: nested_vec_storage_read
    }
}\n```

Storage Namespace

If you want the values in storage to be positioned differently, for instance to avoid collisions with storage from another contract when loading code, you can use the namespace annotation to add a salt to the slot calculations.

```sway\ncontract;

use std::storage::storage_api::{read, write};

// ANCHOR: storage_namespace
storage {
    example_namespace {
        foo: u64 = 0,
    },
    // ANCHOR_END: storage_namespace
}

abi StorageNamespaceExample {
    #[storage(write)]
    fn store_something(amount: u64);

    #[storage(read)]
    fn get_something() -> u64;
}

impl StorageNamespaceExample for Contract {
    #[storage(write)]
    fn store_something(amount: u64) {
        storage.foo.write(amount);
    }

    #[storage(read)]
    fn get_something() -> u64 {
        storage.foo.try_read().unwrap_or(0)
    }
}\n```

Manual Storage Management

It is possible to leverage FuelVM storage operations directly using the std::storage::storage_api::write and std::storage::storage_api::read functions provided in the standard library. With this approach, you will have to manually assign the internal key used for storage. An example is as follows:

```sway\ncontract;

use std::storage::storage_api::{read, write};

abi StorageExample {
    #[storage(write)]
    fn store_something(amount: u64);

    #[storage(read)]
    fn get_something() -> u64;
}

const STORAGE_KEY: b256 = 0x0000000000000000000000000000000000000000000000000000000000000000;

impl StorageExample for Contract {
    #[storage(write)]
    fn store_something(amount: u64) {
        write(STORAGE_KEY, 0, amount);
    }

    #[storage(read)]
    fn get_something() -> u64 {
        let value: Option<u64> = read::<u64>(STORAGE_KEY, 0);
        value.unwrap_or(0)
    }
}\n```

Note: Though these functions can be used for any data type, they should mostly be used for arrays because arrays are not yet supported in storage blocks. Note, however, that all data types can be used as types for keys and/or values in StorageMap<K, V> without any restrictions.

Generic Types

Basics

In Sway, generic types follow a very similar pattern to those in Rust. Let's look at some example syntax, starting with a generic function:

fn noop<T>(argument: T) -> T {
    argument
}

Here, the noop() function trivially returns exactly what was given to it. T is a type parameter, and it says that this function exists for all types T. More formally, this function could be typed as:

noop :: ∀T. T -> T

Generic types are a way to refer to types in general, meaning without specifying a single type. Our noop function would work with any type in the language, so we don't need to specify noop(argument: u8) -> u8, noop(argument: u16) -> u16, etc.

Code Generation

One question that arises when dealing with generic types is: how does the assembly handle this? There are a few approaches to handling generic types at the lowest level. Sway uses a technique called monomorphization. This means that the generic function is compiled to a non-generic version for every type it is called on. In this way, generic functions are purely shorthand for the sake of ergonomics.

Trait Constraints

An important background to know before diving into trait constraints is that the where clause can be used to specify the required traits for the generic argument. So, when writing something like a HashMap you may want to specify that the generic argument implements a Hash trait.

fn get_hashmap_key<T>(key: T) -> b256
    where T: Hash
{
    // Code within here can then call methods associated with the Hash trait on Key
}

Of course, our noop() function is not useful. Often, a programmer will want to declare functions over types which satisfy certain traits. For example, let's try to implement the successor function, successor(), for all numeric types.

fn successor<T>(argument: T)
    where T: Add
{
    argument + 1
}

Run forc build, and you will get:

.. |
 9 |   where T: Add
10 |   {
11 |       argument + 1                                        
   |                  ^ Mismatched types: expected type "T" but saw type "u64"
12 |   }
13 |

This is because we don't know for a fact that 1, which in this case defaulted to 1u64, actually can be added to T. What if T is f64? Or b256? What does it mean to add 1u64 in these cases?

We can solve this problem with another trait constraint. We can only find the successor of some value of type T if that type T defines some incrementor. Let's make a trait:

trait Incrementable {
    /// Returns the value to add when calculating the successor of a value.
    fn incrementor() -> Self;
}

Now, we can modify our successor() function:

fn successor<T>(argument: T)
    where T: Add,
          T: Incrementable
{
    argument + T::incrementor()
}

Generic Structs and Enums

Just like functions, structs and enums can be generic. Let's take a look at the standard library version of Option<T>:

enum Option<T> {
    Some: T,
    None: (),
}

Just like an unconstrained generic function, this type exists for all (∀) types T. Result<T, E> is another example:

enum Result<T, E> {
    Ok: T,
    Err: E,
}

Both generic enums and generic structs can be trait constrained, as well. Consider this struct:

struct Foo<T>
    where T: Add
{
    field_one: T,
}

Type Arguments

Similar to Rust, Sway has what is colloquially known as the turbofish. The turbofish looks like this: ::<> (see the little fish with bubbles behind it?). The turbofish is used to annotate types in a generic context. Say you have the following function:

fn foo<T, E>(t: T) -> Result<T, E> {
    Ok(t)
}

In this code example, which is admittedly asinine, you can't possibly know what type E is. You'd need to provide the type manually, with a turbofish:

fn foo<T, E>(t: T) -> Result<T, E> {
    Ok::<T, MyErrorType>(t)
}

It is also common to see the turbofish used on the function itself:

fn main() {
    foo::<Bar, Baz>()
}

Traits

Declaring a Trait

A trait opts a type into a certain type of behavior or functionality that can be shared among types. This allows for easy reuse of code and generic programming. If you have ever used a typeclass in Haskell, a trait in Rust, or even an interface in Java, these are similar concepts.

Let's take a look at some code:

trait Compare {
    fn equals(self, b: Self) -> bool;
} {
    fn not_equals(self, b: Self) -> bool {
        !self.equals(b)
    }
}

We have just declared a trait called Compare. After the name of the trait, there are two blocks of code (a block is code enclosed in { curly brackets }). The first block is the interface surface. The second block is the methods provided by the trait. If a type can provide the methods in the interface surface, then it gets access to the methods in the trait for free! What the above trait is saying is: if you can determine if two values are equal, then for free, you can determine that they are not equal. Note that trait methods have access to the methods defined in the interface surface.

Implementing a Trait

The example below implements a Compare trait for u64 to check if two numbers are equal. Let's take a look at how that is done:

impl Compare for u64 {
    fn equals(self, b: Self) -> bool {
        self == b
    }
}

The above snippet declares all of the methods in the trait Compare for the type u64. Now, we have access to both the equals and not_equals methods for u64, as long as the trait Compare is in scope.

Supertraits

When using multiple traits, scenarios often come up where one trait may require functionality from another trait. This is where supertraits come in as they allow you to require a trait when implementing another trait, i.e., a trait with a trait. A good example of this is the Ord trait of the std library of Sway. The Ord trait requires the Eq trait, so Eq is kept as a separate trait as one may decide to implement Eq without implementing other parts of the Ord trait.


trait Eq {
    fn equals(self, b: Self) -> bool;
}

trait Ord: Eq {
    fn gte(self, b: Self) -> bool;
}

impl Ord for u64 {
    fn gte(self, b: Self) -> bool {
        // As `Eq` is a supertrait of `Ord`, `Ord` can access the equals method
        self.equals(b) || self.gt(b)
    }
}

To require a supertrait, add a : after the trait name and then list the traits you would like to require and separate them with a +.

ABI supertraits

ABIs can also have supertrait annotations:

```sway\ncontract;

struct Foo {}
impl ABIsupertrait for Foo {
    fn foo() {}
}

trait ABIsupertrait {
    fn foo();
}

abi MyAbi : ABIsupertrait {
    fn bar();
} {
    fn baz() {
        Self::foo() // supertrait method usage
    }
}

impl ABIsupertrait for Contract {
    fn foo() {}
}

// The implementation of MyAbi for Contract must also implement ABIsupertrait
impl MyAbi for Contract {
    fn bar() {
        Self::foo() // supertrait method usage
    }
}\n```

The implementation of MyAbi for Contract must also implement the ABIsupertrait trait. Methods in ABIsupertrait are not available externally, i.e. they're not actually contract methods, but they can be used in the actual contract methods, as shown in the example above.

ABI supertraits are intended to make contract implementations compositional, allowing combining orthogonal contract features using, for instance, libraries.

SuperABIs

In addition to supertraits, ABIs can have superABI annotations:

```sway\ncontract;

abi MySuperAbi {
    fn foo();
}

abi MyAbi : MySuperAbi {
    fn bar();
}

impl MySuperAbi for Contract {
    fn foo() {}
}

// The implementation of MyAbi for Contract must also implement MySuperAbi
impl MyAbi for Contract {
    fn bar() {}
}\n```

The implementation of MyAbi for Contract must also implement the MySuperAbi superABI. Methods in MySuperAbi will be part of the MyAbi contract interface, i.e. will be available externally (and hence cannot be called from other MyAbi contract methods).

SuperABIs are intended to make contract implementations compositional, allowing combining orthogonal contract features using, for instance, libraries.

Associated Items

Traits can declare different kinds of associated items in their interface surface:

Associated functions

Associated functions in traits consist of just function signatures. This indicates that each implementation of the trait for a given type must define all the trait functions.

trait Trait {
    fn associated_fn(self, b: Self) -> bool;
}

Associated constants

Associated constants are constants associated with a type.

trait Trait {
    const ID: u32 = 0;
}

The initializer expression of an associated constants in a trait definition may be omitted to indicate that each implementation of the trait for a given type must specify an initializer:

trait Trait {
    const ID: u32;
}

Check the associated consts section on constants page.

Associated types

Associated types in Sway allow you to define placeholder types within a trait, which can be customized by concrete implementations of that trait. These associated types are used to specify the return types of trait methods or to define type relationships within the trait.

trait MyTrait {
    type AssociatedType;
}

Check the associated types section on associated types page.

Trait Constraints

When writing generic code, you can constraint the choice of types for a generic argument by using the where keyword. The where keyword specifies which traits the concrete generic parameter must implement. In the below example, the function expects_some_trait can be called only if the parameter t is of a type that has SomeTrait implemented. To call the expects_both_traits, parameter t must be of a type that implements both SomeTrait and SomeOtherTrait.

trait SomeTrait { }
trait SomeOtherTrait { }

fn expects_some_trait<T>(t: T) where T: SomeTrait {
    // ...
}

fn expects_some_other_trait<T>(t: T) where T: SomeOtherTrait {
    // ...
}

fn expects_both_traits<T>(t: T) where T: SomeTrait + SomeOtherTrait {
    // ...
}

Marker Traits

Sway types can be classified in various ways according to their intrinsic properties. These classifications are represented as marker traits. Marker traits are implemented by the compiler and cannot be explicitly implemented in code.

E.g., all types whose instances can be used in the panic expression automatically implement the Error marker trait. We can use that trait, e.g., to specify that a generic argument must be compatible with the panic expression:

fn panic_with_error<E>(err: E) where E: Error {
    panic err;
}

Note panic expression and error types have not yet been implemented

All marker traits are defined in the std::marker module.

Use Cases

Custom Types (structs, enums)

Often, libraries and APIs have interfaces that are abstracted over a type that implements a certain trait. It is up to the consumer of the interface to implement that trait for the type they wish to use with the interface. For example, let's take a look at a trait and an interface built off of it.

library;

pub enum Suit {
    Hearts: (),
    Diamonds: (),
    Clubs: (),
    Spades: (),
}

pub trait Card {
    fn suit(self) -> Suit;
    fn value(self) -> u8;
}

fn play_game_with_deck<T>(a: Vec<T>) where T: Card {
    // insert some creative card game here
}

Now, if you want to use the function play_game_with_deck with your struct, you must implement Card for your struct. Note that the following code example assumes a dependency games has been included in the Forc.toml file.

script;

use games::*;

struct MyCard {
    suit: Suit,
    value: u8
}

impl Card for MyCard {
    fn suit(self) -> Suit {
        self.suit
    }
    fn value(self) -> u8 {
        self.value
    }
}

fn main() {
    let mut i = 52;
    let mut deck: Vec<MyCard> = Vec::with_capacity(50);
    while i > 0 {
        i = i - 1;
        deck.push(MyCard { suit: generate_random_suit(), value: i % 4}
    }
    play_game_with_deck(deck);
}

fn generate_random_suit() -> Suit {
  [ ... ]
}

Associated Types

Associated types in Sway allow you to define placeholder types within a trait, which can be customized by concrete implementations of that trait. These associated types are used to specify the return types of trait methods or to define type relationships within the trait.

Associated types are a powerful feature of Sway's trait system, enabling generic programming and abstraction over types. They help improve code clarity and maintainability by allowing you to define generic traits without committing to specific types.

Declaring Associated Types

Associated types are declared within a trait using the type keyword. Here's the syntax for declaring an associated type:

trait MyTrait {
    type AssociatedType;
}

Implementing Associated Types

Concrete implementations of a trait with associated types must provide a specific type for each associated type defined in the trait. Here's an example of implementing a trait with an associated type:

struct MyStruct;

impl MyTrait for MyStruct {
    type AssociatedType = u32; // Implementing the associated type with u32
}

In this example, MyStruct implements MyTrait and specifies that the associated type AssociatedType is u32.

Using Associated Types

Associated types are used within trait methods or where the trait is used as a bound for generic functions or structs. You can use the associated type like any other type. Here's an example:

trait MyTrait {
    type AssociatedType;
    
    fn get_value(self) -> Self::AssociatedType;
}

struct MyStruct;

impl MyTrait for MyStruct {
    type AssociatedType = u32;

    fn get_value(self) -> Self::AssociatedType {
        42
    }
}

In this example, get_value is a trait method that returns an associated type AssociatedType.

Use Cases

Associated types are particularly useful in scenarios where you want to define traits that work with different types of data structures or abstractions, allowing the implementer to specify the concrete types. Some common use cases include:

  • Collections: Traits for generic collections that allow users to specify the type of elements.
  • Iterator Patterns: Traits for implementing iterators with varying element types.
  • Serialization and Deserialization: Traits for serializing and deserializing data with different data formats.

Generics and Trait Constraints

Generics as Constraints

At a high level, Sway allows you to define constraints, or restrictions, that allow you to strike a balance between writing abstract and reusable code and enforcing compile-time checks to determine if the abstract code that you've written is correct.

The "abstract and reusable" part largely comes from generic types and the "enforcing compile-time checks" part largely comes from trait constraints. Generic types can be used with functions, structs, and enums (as we have seen in this book), but they can also be used with traits.

Generic Traits

Combining generic types with traits allows you to write abstract and reusable traits that can be implemented for any number of data types.

For example, imagine that you want to write a trait for converting between different types. This would be similar to Rust's Into and From traits. In Sway your conversion trait would look something like:

```sway\nlibrary;

// ANCHOR: trait_definition
trait Convert<T> {
    fn from(t: T) -> Self;
}
// ANCHOR_END: trait_definition

// ANCHOR: trait_impl
struct Square {
    width: u64,
}

struct Rectangle {
    width: u64,
    length: u64,
}

impl Convert<Square> for Rectangle {
    fn from(t: Square) -> Self {
        Self {
            width: t.width,
            length: t.width,
        }
    }
}
// ANCHOR_END: trait_impl

// ANCHOR: trait_usage
fn main() {
    let s = Square { width: 5 };
    let r = Rectangle::from(s);
}
// ANCHOR_END: trait_usage

// ANCHOR: trait_constraint
fn into_rectangle<T>(t: T) -> Rectangle
where
    Rectangle: Convert<T>,
{
    Rectangle::from(t)
}
// ANCHOR_END: trait_constraint\n```

The trait Convert takes a generic type T. Convert has one method from, which takes one parameter of type T and returns a Self. This means that when you implement Convert for a data type, from will return the type of that data type but will take as input the type that you define as T. Here is an example:

```sway\nlibrary;

// ANCHOR: trait_definition
trait Convert<T> {
    fn from(t: T) -> Self;
}
// ANCHOR_END: trait_definition

// ANCHOR: trait_impl
struct Square {
    width: u64,
}

struct Rectangle {
    width: u64,
    length: u64,
}

impl Convert<Square> for Rectangle {
    fn from(t: Square) -> Self {
        Self {
            width: t.width,
            length: t.width,
        }
    }
}
// ANCHOR_END: trait_impl

// ANCHOR: trait_usage
fn main() {
    let s = Square { width: 5 };
    let r = Rectangle::from(s);
}
// ANCHOR_END: trait_usage

// ANCHOR: trait_constraint
fn into_rectangle<T>(t: T) -> Rectangle
where
    Rectangle: Convert<T>,
{
    Rectangle::from(t)
}
// ANCHOR_END: trait_constraint\n```

In this example, you have two different data types, Square and Rectangle. You know that all squares are rectangles and thus Square can convert into Rectangle (but not vice versa) and thus you can implement the conversion trait for those types.

If we want to call these methods we can do so by:

```sway\nlibrary;

// ANCHOR: trait_definition
trait Convert<T> {
    fn from(t: T) -> Self;
}
// ANCHOR_END: trait_definition

// ANCHOR: trait_impl
struct Square {
    width: u64,
}

struct Rectangle {
    width: u64,
    length: u64,
}

impl Convert<Square> for Rectangle {
    fn from(t: Square) -> Self {
        Self {
            width: t.width,
            length: t.width,
        }
    }
}
// ANCHOR_END: trait_impl

// ANCHOR: trait_usage
fn main() {
    let s = Square { width: 5 };
    let r = Rectangle::from(s);
}
// ANCHOR_END: trait_usage

// ANCHOR: trait_constraint
fn into_rectangle<T>(t: T) -> Rectangle
where
    Rectangle: Convert<T>,
{
    Rectangle::from(t)
}
// ANCHOR_END: trait_constraint\n```

Trait Constraints

Trait constraints allow you to use generic types and traits to place constraints on what abstract code you are willing to accept in your program as correct. These constraints take the form of compile-time checks for correctness.

If we wanted to use trait constraints with our Convert trait from the previous section we could do so like so:

```sway\nlibrary;

// ANCHOR: trait_definition
trait Convert<T> {
    fn from(t: T) -> Self;
}
// ANCHOR_END: trait_definition

// ANCHOR: trait_impl
struct Square {
    width: u64,
}

struct Rectangle {
    width: u64,
    length: u64,
}

impl Convert<Square> for Rectangle {
    fn from(t: Square) -> Self {
        Self {
            width: t.width,
            length: t.width,
        }
    }
}
// ANCHOR_END: trait_impl

// ANCHOR: trait_usage
fn main() {
    let s = Square { width: 5 };
    let r = Rectangle::from(s);
}
// ANCHOR_END: trait_usage

// ANCHOR: trait_constraint
fn into_rectangle<T>(t: T) -> Rectangle
where
    Rectangle: Convert<T>,
{
    Rectangle::from(t)
}
// ANCHOR_END: trait_constraint\n```

This function allows you to take any generic data type T and convert it to the type Rectangle as long as Convert<T> is implemented for Rectangle. Calling this function with a type T for which Convert<T> is not implemented for Rectangle will fail Sway's compile-time checks.

Inline Assembly in Sway

While many users will never have to touch assembly language while writing Sway code, it is a powerful tool that enables many advanced use-cases (e.g., optimizations, building libraries, etc).

ASM Block

In Sway, the way we use assembly inline is to declare an asm block like this:

asm() {...}

Declaring an asm block is similar to declaring a function. We can specify register names to operate on as arguments, we can perform assembly instructions within the block, and we can return a value by specifying a return register. Here's an example showing what this might look like:

pub fn add_1(num: u32) -> u32 {
    asm(r1: num, r2) {
        add r2 r1 one;
        r2: u32
    }
}

The return register is specified at the end of the asm block, after all the assembly instructions. It consists of the register name and an optional return type. In the above example, the return register name is r2 and the return type is u32. If the return type is omitted, it is u64 by default.

The return register itself is optional. If it is not specified, similar to functions, the returned value from the asm block will be unit, ().

An asm block can only return a single register. If you really need to return more than one value, you can modify a tuple. Here's an example showing how you can implement this for (u64, u64):

```sway\nscript;

fn adder(a: u64, b: u64, c: u64) -> (u64, u64) {
    let empty_tuple = (0u64, 0u64);
    asm(output: empty_tuple, r1: a, r2: b, r3: c, r4, r5) {
        add r4 r1 r2; // add a & b and put the result in r4
        add r5 r2 r3; // add b & c and put the result in r5
        sw output r4 i0; // store the word in r4 in output + 0 words
        sw output r5 i1; // store the word in r5 in output + 1 word
        output: (u64, u64) // return both values
    }
}

fn main() -> bool {
    let (first, second) = adder(1, 2, 3);
    assert(first == 3);
    assert(second == 5);
    true
}\n```

Note that this is contrived example meant to demonstrate the syntax; there's absolutely no need to use assembly to add integers!

Note that in the above example:

  • we initialized the register r1 with the value of num.
  • we declared a second register r2 (you may choose any register names you want).
  • we use the add opcode to add one to the value of r1 and store it in r2.
  • one is an example of a "reserved register", of which there are 16 in total. Further reading on this is linked below under "Semantics".
  • we return r2 and specify the return type as being u32.

An important note is that the ji and jnei opcodes are not available within an asm block. For those looking to introduce control flow to asm blocks, it is recommended to surround smaller chunks of asm with control flow (if, else, and while).

For examples of assembly in action, check out the Sway standard library.

For a complete list of all instructions supported in the FuelVM: Instructions.

And to learn more about the FuelVM semantics: Semantics.

Never Type

The Never type ! represents the type of computations which never resolve to any value at all.

Additional Information

break, continue and return expressions also have type !. For example we are allowed to write:

let x: ! = {
    return 123
};

Although the let is pointless here, it illustrates the meaning of !. Since x is never assigned a value (because return returns from the entire function), x can be given type Never. We could also replace return 123 with a revert() or a never-ending loop and this code would still be valid.

A more realistic usage of Never is in this code:

let num: u32 = match get_a_number() {
    Some(num) => num,
    None => break,
};

Both match arms must produce values of type [u32], but since break never produces a value at all we know it can never produce a value which isn't a [u32]. This illustrates another behaviour of the ! type - expressions with type ! will coerce into any other type.

Note that ! type coerces into any other type, another example of this would be:

let x: u32 = {
    return 123
};

Regardless of the type of x, the return block of type Never will always coerce into x type.

Examples

fn foo() {
    let num: u64 = match Option::None::<u64> {
        Some(num) => num,
        None => return,
    };
}

Common Collections

Sway’s standard library includes a number of very useful data structures called collections. Most other data types represent one specific value, but collections can contain multiple values. Unlike the built-in array and tuple types which are allocated on the "stack" and cannot grow in size, the data these collections point to is stored either on the "heap" or in contract "storage", which means the amount of data does not need to be known at compile time and can grow as the program runs. Each kind of collection has different capabilities and costs, and choosing an appropriate one for your current situation is a skill you’ll develop over time. In this chapter, we’ll discuss three collections that are used very often in Sway programs:

A vector on the heap allows you to store a variable number of values next to each other.

A StorageVec is similar to a vector on the heap but uses persistent storage.

A StorageMap allows you to associate a value with a particular key.

We’ll discuss how to create and update a vector, StorageVec, and StorageMap, as well as what makes each special.

Vectors on the Heap

The first collection type we’ll look at is Vec<T>, also known as a vector. Vectors allow you to store more than one value in a single data structure that puts all the values next to each other in memory. Vectors can only store values of the same type. They are useful when you have a list of items, such as the lines of text in a file or the prices of items in a shopping cart.

Vec<T> is included in the standard library prelude which means that there is no need to import it manually.

Creating a New Vector

To create a new empty vector, we call the Vec::new function, as shown below:

```sway\nscript;

fn main() {
    // ANCHOR: vec_new
    let v: Vec<u64> = Vec::new();
    // ANCHOR_END: vec_new
    // ANCHOR: vec_push
    let mut v = Vec::new();

    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
    // ANCHOR_END: vec_push
    // ANCHOR: vec_get
    let third = v.get(2);
    match third {
        Some(third) => log(third),
        None => revert(42),
    }
    // ANCHOR_END: vec_get
    // ANCHOR: vec_get_oob
    let does_not_exist = v.get(100);
    // ...decide here how to handle an out-of-bounds access
    // ANCHOR_END: vec_get_oob
    // ANCHOR: vec_iterate_while
    let mut i = 0;
    while i < v.len() {
        log(v.get(i).unwrap());
        i += 1;
    }
    // ANCHOR_END: vec_iterate_while
    // ANCHOR: vec_iterate_for
    for elem in v.iter() {
        log(elem);
    }
    // ANCHOR_END: vec_iterate_for
    // ANCHOR: vec_iterate_for_undefined
    for elem in v.iter() {
        log(elem);
        if elem == 3 {
            v.push(6); // Modification causes undefined behavior!
        }
    }
    // ANCHOR_END: vec_iterate_for_undefined
    // ANCHOR: vec_iterate_custom
    // Start from the end
    let mut i = v.len() - 1;
    while 0 <= i {
        log(v.get(i).unwrap());
        // Access every second element
        i -= 2;
    }
    // ANCHOR_END: vec_iterate_custom
    // ANCHOR: vec_multiple_data_types
    enum TableCell {
        Int: u64,
        B256: b256,
        Boolean: bool,
    }

    let mut row = Vec::new();
    row.push(TableCell::Int(3));
    row.push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
    row.push(TableCell::Boolean(true));
    // ANCHOR_END: vec_multiple_data_types
}\n```

Note that we added a type annotation here. Because we aren’t inserting any values into this vector, the Sway compiler doesn’t know what kind of elements we intend to store. Vectors are implemented using generics which means that the Vec<T> type provided by the standard library can hold any type. When we create a vector to hold a specific type, we can specify the type within angle brackets. In the example above, we’ve told the Sway compiler that the Vec<T> in v will hold elements of the u64 type.

Updating a Vector

To create a vector and then add elements to it, we can use the push method, as shown below:

```sway\nscript;

fn main() {
    // ANCHOR: vec_new
    let v: Vec<u64> = Vec::new();
    // ANCHOR_END: vec_new
    // ANCHOR: vec_push
    let mut v = Vec::new();

    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
    // ANCHOR_END: vec_push
    // ANCHOR: vec_get
    let third = v.get(2);
    match third {
        Some(third) => log(third),
        None => revert(42),
    }
    // ANCHOR_END: vec_get
    // ANCHOR: vec_get_oob
    let does_not_exist = v.get(100);
    // ...decide here how to handle an out-of-bounds access
    // ANCHOR_END: vec_get_oob
    // ANCHOR: vec_iterate_while
    let mut i = 0;
    while i < v.len() {
        log(v.get(i).unwrap());
        i += 1;
    }
    // ANCHOR_END: vec_iterate_while
    // ANCHOR: vec_iterate_for
    for elem in v.iter() {
        log(elem);
    }
    // ANCHOR_END: vec_iterate_for
    // ANCHOR: vec_iterate_for_undefined
    for elem in v.iter() {
        log(elem);
        if elem == 3 {
            v.push(6); // Modification causes undefined behavior!
        }
    }
    // ANCHOR_END: vec_iterate_for_undefined
    // ANCHOR: vec_iterate_custom
    // Start from the end
    let mut i = v.len() - 1;
    while 0 <= i {
        log(v.get(i).unwrap());
        // Access every second element
        i -= 2;
    }
    // ANCHOR_END: vec_iterate_custom
    // ANCHOR: vec_multiple_data_types
    enum TableCell {
        Int: u64,
        B256: b256,
        Boolean: bool,
    }

    let mut row = Vec::new();
    row.push(TableCell::Int(3));
    row.push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
    row.push(TableCell::Boolean(true));
    // ANCHOR_END: vec_multiple_data_types
}\n```

As with any variable, if we want to be able to change its value, we need to make it mutable using the mut keyword, as discussed in the section Declaring a Variable. The numbers we place inside are all of type u64, and the Sway compiler infers this from the data, so we don’t need the Vec<u64> annotation.

Reading Elements of Vectors

To read a value stored in a vector at a particular index, you can use the get method as shown below:

```sway\nscript;

fn main() {
    // ANCHOR: vec_new
    let v: Vec<u64> = Vec::new();
    // ANCHOR_END: vec_new
    // ANCHOR: vec_push
    let mut v = Vec::new();

    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
    // ANCHOR_END: vec_push
    // ANCHOR: vec_get
    let third = v.get(2);
    match third {
        Some(third) => log(third),
        None => revert(42),
    }
    // ANCHOR_END: vec_get
    // ANCHOR: vec_get_oob
    let does_not_exist = v.get(100);
    // ...decide here how to handle an out-of-bounds access
    // ANCHOR_END: vec_get_oob
    // ANCHOR: vec_iterate_while
    let mut i = 0;
    while i < v.len() {
        log(v.get(i).unwrap());
        i += 1;
    }
    // ANCHOR_END: vec_iterate_while
    // ANCHOR: vec_iterate_for
    for elem in v.iter() {
        log(elem);
    }
    // ANCHOR_END: vec_iterate_for
    // ANCHOR: vec_iterate_for_undefined
    for elem in v.iter() {
        log(elem);
        if elem == 3 {
            v.push(6); // Modification causes undefined behavior!
        }
    }
    // ANCHOR_END: vec_iterate_for_undefined
    // ANCHOR: vec_iterate_custom
    // Start from the end
    let mut i = v.len() - 1;
    while 0 <= i {
        log(v.get(i).unwrap());
        // Access every second element
        i -= 2;
    }
    // ANCHOR_END: vec_iterate_custom
    // ANCHOR: vec_multiple_data_types
    enum TableCell {
        Int: u64,
        B256: b256,
        Boolean: bool,
    }

    let mut row = Vec::new();
    row.push(TableCell::Int(3));
    row.push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
    row.push(TableCell::Boolean(true));
    // ANCHOR_END: vec_multiple_data_types
}\n```

Note two details here. First, we use the index value of 2 to get the third element because vectors are indexed by number, starting at zero. Second, we get the third element by using the get method with the index passed as an argument, which gives us an Option<T>.

When the get method is passed an index that is outside the vector, it returns None without panicking. This is particularly useful if accessing an element beyond the range of the vector may happen occasionally under normal circumstances. Your code will then have logic to handle having either Some(element) or None. For example, the index could be coming as a contract method argument. If the argument passed is too large, the method get will return a None value, and the contract method may then decide to revert when that happens or return a meaningful error that tells the user how many items are in the current vector and give them another chance to pass a valid value.

Iterating over the Values in a Vector

To access elements of a vector, we can iterate through the valid indices using a while loop and the len method as shown below:

```sway\nscript;

fn main() {
    // ANCHOR: vec_new
    let v: Vec<u64> = Vec::new();
    // ANCHOR_END: vec_new
    // ANCHOR: vec_push
    let mut v = Vec::new();

    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
    // ANCHOR_END: vec_push
    // ANCHOR: vec_get
    let third = v.get(2);
    match third {
        Some(third) => log(third),
        None => revert(42),
    }
    // ANCHOR_END: vec_get
    // ANCHOR: vec_get_oob
    let does_not_exist = v.get(100);
    // ...decide here how to handle an out-of-bounds access
    // ANCHOR_END: vec_get_oob
    // ANCHOR: vec_iterate_while
    let mut i = 0;
    while i < v.len() {
        log(v.get(i).unwrap());
        i += 1;
    }
    // ANCHOR_END: vec_iterate_while
    // ANCHOR: vec_iterate_for
    for elem in v.iter() {
        log(elem);
    }
    // ANCHOR_END: vec_iterate_for
    // ANCHOR: vec_iterate_for_undefined
    for elem in v.iter() {
        log(elem);
        if elem == 3 {
            v.push(6); // Modification causes undefined behavior!
        }
    }
    // ANCHOR_END: vec_iterate_for_undefined
    // ANCHOR: vec_iterate_custom
    // Start from the end
    let mut i = v.len() - 1;
    while 0 <= i {
        log(v.get(i).unwrap());
        // Access every second element
        i -= 2;
    }
    // ANCHOR_END: vec_iterate_custom
    // ANCHOR: vec_multiple_data_types
    enum TableCell {
        Int: u64,
        B256: b256,
        Boolean: bool,
    }

    let mut row = Vec::new();
    row.push(TableCell::Int(3));
    row.push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
    row.push(TableCell::Boolean(true));
    // ANCHOR_END: vec_multiple_data_types
}\n```

Note two details here. First, we use the method len which returns the length of the vector. Second, we call the method unwrap to extract the Option returned by get. We know that unwrap will not fail (i.e. will not cause a revert) because each index i passed to get is known to be smaller than the length of the vector.

The idiomatic and convenient way to access each element in a vector in turn, is to use the for loop in the combination with the iter method. The iter method returns an iterator that iterates over all the elements of the vector sequentially.

```sway\nscript;

fn main() {
    // ANCHOR: vec_new
    let v: Vec<u64> = Vec::new();
    // ANCHOR_END: vec_new
    // ANCHOR: vec_push
    let mut v = Vec::new();

    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
    // ANCHOR_END: vec_push
    // ANCHOR: vec_get
    let third = v.get(2);
    match third {
        Some(third) => log(third),
        None => revert(42),
    }
    // ANCHOR_END: vec_get
    // ANCHOR: vec_get_oob
    let does_not_exist = v.get(100);
    // ...decide here how to handle an out-of-bounds access
    // ANCHOR_END: vec_get_oob
    // ANCHOR: vec_iterate_while
    let mut i = 0;
    while i < v.len() {
        log(v.get(i).unwrap());
        i += 1;
    }
    // ANCHOR_END: vec_iterate_while
    // ANCHOR: vec_iterate_for
    for elem in v.iter() {
        log(elem);
    }
    // ANCHOR_END: vec_iterate_for
    // ANCHOR: vec_iterate_for_undefined
    for elem in v.iter() {
        log(elem);
        if elem == 3 {
            v.push(6); // Modification causes undefined behavior!
        }
    }
    // ANCHOR_END: vec_iterate_for_undefined
    // ANCHOR: vec_iterate_custom
    // Start from the end
    let mut i = v.len() - 1;
    while 0 <= i {
        log(v.get(i).unwrap());
        // Access every second element
        i -= 2;
    }
    // ANCHOR_END: vec_iterate_custom
    // ANCHOR: vec_multiple_data_types
    enum TableCell {
        Int: u64,
        B256: b256,
        Boolean: bool,
    }

    let mut row = Vec::new();
    row.push(TableCell::Int(3));
    row.push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
    row.push(TableCell::Boolean(true));
    // ANCHOR_END: vec_multiple_data_types
}\n```

Note that modifying a vector during iteration, by e.g. adding or removing elements, is a logical error and results in an undefined behavior:

```sway\nscript;

fn main() {
    // ANCHOR: vec_new
    let v: Vec<u64> = Vec::new();
    // ANCHOR_END: vec_new
    // ANCHOR: vec_push
    let mut v = Vec::new();

    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
    // ANCHOR_END: vec_push
    // ANCHOR: vec_get
    let third = v.get(2);
    match third {
        Some(third) => log(third),
        None => revert(42),
    }
    // ANCHOR_END: vec_get
    // ANCHOR: vec_get_oob
    let does_not_exist = v.get(100);
    // ...decide here how to handle an out-of-bounds access
    // ANCHOR_END: vec_get_oob
    // ANCHOR: vec_iterate_while
    let mut i = 0;
    while i < v.len() {
        log(v.get(i).unwrap());
        i += 1;
    }
    // ANCHOR_END: vec_iterate_while
    // ANCHOR: vec_iterate_for
    for elem in v.iter() {
        log(elem);
    }
    // ANCHOR_END: vec_iterate_for
    // ANCHOR: vec_iterate_for_undefined
    for elem in v.iter() {
        log(elem);
        if elem == 3 {
            v.push(6); // Modification causes undefined behavior!
        }
    }
    // ANCHOR_END: vec_iterate_for_undefined
    // ANCHOR: vec_iterate_custom
    // Start from the end
    let mut i = v.len() - 1;
    while 0 <= i {
        log(v.get(i).unwrap());
        // Access every second element
        i -= 2;
    }
    // ANCHOR_END: vec_iterate_custom
    // ANCHOR: vec_multiple_data_types
    enum TableCell {
        Int: u64,
        B256: b256,
        Boolean: bool,
    }

    let mut row = Vec::new();
    row.push(TableCell::Int(3));
    row.push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
    row.push(TableCell::Boolean(true));
    // ANCHOR_END: vec_multiple_data_types
}\n```

Accessing vector elements via while loop should be used only when more control over traversal is needed. E.g., in the below example we iterate the vector backwards, accessing only every second element.

```sway\nscript;

fn main() {
    // ANCHOR: vec_new
    let v: Vec<u64> = Vec::new();
    // ANCHOR_END: vec_new
    // ANCHOR: vec_push
    let mut v = Vec::new();

    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
    // ANCHOR_END: vec_push
    // ANCHOR: vec_get
    let third = v.get(2);
    match third {
        Some(third) => log(third),
        None => revert(42),
    }
    // ANCHOR_END: vec_get
    // ANCHOR: vec_get_oob
    let does_not_exist = v.get(100);
    // ...decide here how to handle an out-of-bounds access
    // ANCHOR_END: vec_get_oob
    // ANCHOR: vec_iterate_while
    let mut i = 0;
    while i < v.len() {
        log(v.get(i).unwrap());
        i += 1;
    }
    // ANCHOR_END: vec_iterate_while
    // ANCHOR: vec_iterate_for
    for elem in v.iter() {
        log(elem);
    }
    // ANCHOR_END: vec_iterate_for
    // ANCHOR: vec_iterate_for_undefined
    for elem in v.iter() {
        log(elem);
        if elem == 3 {
            v.push(6); // Modification causes undefined behavior!
        }
    }
    // ANCHOR_END: vec_iterate_for_undefined
    // ANCHOR: vec_iterate_custom
    // Start from the end
    let mut i = v.len() - 1;
    while 0 <= i {
        log(v.get(i).unwrap());
        // Access every second element
        i -= 2;
    }
    // ANCHOR_END: vec_iterate_custom
    // ANCHOR: vec_multiple_data_types
    enum TableCell {
        Int: u64,
        B256: b256,
        Boolean: bool,
    }

    let mut row = Vec::new();
    row.push(TableCell::Int(3));
    row.push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
    row.push(TableCell::Boolean(true));
    // ANCHOR_END: vec_multiple_data_types
}\n```

Using an Enum to store Multiple Types

Vectors can only store values that are the same type. This can be inconvenient; there are definitely use cases for needing to store a list of items of different types. Fortunately, the variants of an enum are defined under the same enum type, so when we need one type to represent elements of different types, we can define and use an enum!

For example, say we want to get values from a row in a table in which some of the columns in the row contain integers, some b256 values, and some Booleans. We can define an enum whose variants will hold the different value types, and all the enum variants will be considered the same type: that of the enum. Then we can create a vector to hold that enum and so, ultimately, holds different types. We’ve demonstrated this below:

```sway\nscript;

fn main() {
    // ANCHOR: vec_new
    let v: Vec<u64> = Vec::new();
    // ANCHOR_END: vec_new
    // ANCHOR: vec_push
    let mut v = Vec::new();

    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
    // ANCHOR_END: vec_push
    // ANCHOR: vec_get
    let third = v.get(2);
    match third {
        Some(third) => log(third),
        None => revert(42),
    }
    // ANCHOR_END: vec_get
    // ANCHOR: vec_get_oob
    let does_not_exist = v.get(100);
    // ...decide here how to handle an out-of-bounds access
    // ANCHOR_END: vec_get_oob
    // ANCHOR: vec_iterate_while
    let mut i = 0;
    while i < v.len() {
        log(v.get(i).unwrap());
        i += 1;
    }
    // ANCHOR_END: vec_iterate_while
    // ANCHOR: vec_iterate_for
    for elem in v.iter() {
        log(elem);
    }
    // ANCHOR_END: vec_iterate_for
    // ANCHOR: vec_iterate_for_undefined
    for elem in v.iter() {
        log(elem);
        if elem == 3 {
            v.push(6); // Modification causes undefined behavior!
        }
    }
    // ANCHOR_END: vec_iterate_for_undefined
    // ANCHOR: vec_iterate_custom
    // Start from the end
    let mut i = v.len() - 1;
    while 0 <= i {
        log(v.get(i).unwrap());
        // Access every second element
        i -= 2;
    }
    // ANCHOR_END: vec_iterate_custom
    // ANCHOR: vec_multiple_data_types
    enum TableCell {
        Int: u64,
        B256: b256,
        Boolean: bool,
    }

    let mut row = Vec::new();
    row.push(TableCell::Int(3));
    row.push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
    row.push(TableCell::Boolean(true));
    // ANCHOR_END: vec_multiple_data_types
}\n```

Now that we’ve discussed some of the most common ways to use vectors, be sure to review the API documentation for all the many useful methods defined on Vec<T> by the standard library. For now, these can be found in the source code for Vec<T>. For example, in addition to push, a pop method removes and returns the last element, a remove method removes and returns the element at some chosen index within the vector, an insert method inserts an element at some chosen index within the vector, etc.

Storage Vectors

The second collection type we’ll look at is StorageVec<T>. Just like vectors on the heap (i.e. Vec<T>), storage vectors allow you to store more than one value in a single data structure where each value is assigned an index and can only store values of the same type. However, unlike Vec<T>, the elements of a StorageVec are stored in persistent storage, and consecutive elements are not necessarily stored in storage slots that have consecutive keys.

In order to use StorageVec<T>, you must first import StorageVec as follows:

```sway\ncontract;

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR_END: storage_vec_import
// ANCHOR: storage_vec_multiple_types_enum
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}
// ANCHOR_END: storage_vec_multiple_types_enum
storage {
    // ANCHOR: storage_vec_decl
    v: StorageVec<u64> = StorageVec {},
    // ANCHOR_END: storage_vec_decl
    // ANCHOR: storage_vec_multiple_types_decl
    row: StorageVec<TableCell> = StorageVec {},
    // ANCHOR_END: storage_vec_multiple_types_decl
    // ANCHOR: storage_vec_nested
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},
    // ANCHOR_END: storage_vec_nested
}

abi StorageVecContract {
    #[storage(read, write)]
    fn push_to_storage_vec();

    #[storage(read)]
    fn read_from_storage_vec();

    #[storage(read)]
    fn iterate_over_a_storage_vec();

    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec();

    #[storage(read, write)]
    fn access_nested_vec();
}

impl StorageVecContract for Contract {
    // ANCHOR: storage_vec_push
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }
    // ANCHOR_END: storage_vec_push
    // ANCHOR: storage_vec_get
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Some(third) => log(third.read()),
            None => revert(42),
        }
    }
    // ANCHOR_END: storage_vec_get
    // ANCHOR: storage_vec_iterate
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        // Iterate over all the elements
        // in turn using the `while` loop.
        // **This approach is not recommended.**
        // For iterating over all the elements
        // in turn use the `for` loop instead.
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }

        // The preferred and most performant way
        // to iterate over all the elements in turn is
        // to use the `for` loop.
        for elem in storage.v.iter() {
            log(elem.read());
        }

        // Use the `while` loop only when more
        // control over traversal is needed.
        // E.g., in the below example we iterate
        // the vector backwards, accessing only
        // every second element.
        let mut i = storage.v.len() - 1;
        while 0 <= i {
            log(storage.v.get(i).unwrap().read());
            i -= 2;
        }
    }
    // ANCHOR_END: storage_vec_iterate
    // ANCHOR: storage_vec_multiple_types_fn
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage
            .row
            .push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }
    // ANCHOR_END: storage_vec_multiple_types_fn

    // ANCHOR: access_nested_vec 
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }
    // ANCHOR_END: access_nested_vec 
}\n```

Another major difference between Vec<T> and StorageVec<T> is that StorageVec<T> can only be used in a contract because only contracts are allowed to access persistent storage.

Creating a New StorageVec

To create a new empty StorageVec, we have to declare the vector in a storage block as follows:

```sway\ncontract;

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR_END: storage_vec_import
// ANCHOR: storage_vec_multiple_types_enum
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}
// ANCHOR_END: storage_vec_multiple_types_enum
storage {
    // ANCHOR: storage_vec_decl
    v: StorageVec<u64> = StorageVec {},
    // ANCHOR_END: storage_vec_decl
    // ANCHOR: storage_vec_multiple_types_decl
    row: StorageVec<TableCell> = StorageVec {},
    // ANCHOR_END: storage_vec_multiple_types_decl
    // ANCHOR: storage_vec_nested
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},
    // ANCHOR_END: storage_vec_nested
}

abi StorageVecContract {
    #[storage(read, write)]
    fn push_to_storage_vec();

    #[storage(read)]
    fn read_from_storage_vec();

    #[storage(read)]
    fn iterate_over_a_storage_vec();

    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec();

    #[storage(read, write)]
    fn access_nested_vec();
}

impl StorageVecContract for Contract {
    // ANCHOR: storage_vec_push
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }
    // ANCHOR_END: storage_vec_push
    // ANCHOR: storage_vec_get
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Some(third) => log(third.read()),
            None => revert(42),
        }
    }
    // ANCHOR_END: storage_vec_get
    // ANCHOR: storage_vec_iterate
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        // Iterate over all the elements
        // in turn using the `while` loop.
        // **This approach is not recommended.**
        // For iterating over all the elements
        // in turn use the `for` loop instead.
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }

        // The preferred and most performant way
        // to iterate over all the elements in turn is
        // to use the `for` loop.
        for elem in storage.v.iter() {
            log(elem.read());
        }

        // Use the `while` loop only when more
        // control over traversal is needed.
        // E.g., in the below example we iterate
        // the vector backwards, accessing only
        // every second element.
        let mut i = storage.v.len() - 1;
        while 0 <= i {
            log(storage.v.get(i).unwrap().read());
            i -= 2;
        }
    }
    // ANCHOR_END: storage_vec_iterate
    // ANCHOR: storage_vec_multiple_types_fn
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage
            .row
            .push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }
    // ANCHOR_END: storage_vec_multiple_types_fn

    // ANCHOR: access_nested_vec 
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }
    // ANCHOR_END: access_nested_vec 
}\n```

Just like any other storage variable, two things are required when declaring a StorageVec: a type annotation and an initializer. The initializer is just an empty struct of type StorageVec because StorageVec<T> itself is an empty struct! Everything that is interesting about StorageVec<T> is implemented in its methods.

Storage vectors, just like Vec<T>, are implemented using generics which means that the StorageVec<T> type provided by the standard library can hold any type. When we create a StorageVec to hold a specific type, we can specify the type within angle brackets. In the example above, we’ve told the Sway compiler that the StorageVec<T> in v will hold elements of the u64 type.

Updating a StorageVec

To add elements to a StorageVec, we can use the push method, as shown below:

```sway\ncontract;

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR_END: storage_vec_import
// ANCHOR: storage_vec_multiple_types_enum
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}
// ANCHOR_END: storage_vec_multiple_types_enum
storage {
    // ANCHOR: storage_vec_decl
    v: StorageVec<u64> = StorageVec {},
    // ANCHOR_END: storage_vec_decl
    // ANCHOR: storage_vec_multiple_types_decl
    row: StorageVec<TableCell> = StorageVec {},
    // ANCHOR_END: storage_vec_multiple_types_decl
    // ANCHOR: storage_vec_nested
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},
    // ANCHOR_END: storage_vec_nested
}

abi StorageVecContract {
    #[storage(read, write)]
    fn push_to_storage_vec();

    #[storage(read)]
    fn read_from_storage_vec();

    #[storage(read)]
    fn iterate_over_a_storage_vec();

    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec();

    #[storage(read, write)]
    fn access_nested_vec();
}

impl StorageVecContract for Contract {
    // ANCHOR: storage_vec_push
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }
    // ANCHOR_END: storage_vec_push
    // ANCHOR: storage_vec_get
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Some(third) => log(third.read()),
            None => revert(42),
        }
    }
    // ANCHOR_END: storage_vec_get
    // ANCHOR: storage_vec_iterate
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        // Iterate over all the elements
        // in turn using the `while` loop.
        // **This approach is not recommended.**
        // For iterating over all the elements
        // in turn use the `for` loop instead.
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }

        // The preferred and most performant way
        // to iterate over all the elements in turn is
        // to use the `for` loop.
        for elem in storage.v.iter() {
            log(elem.read());
        }

        // Use the `while` loop only when more
        // control over traversal is needed.
        // E.g., in the below example we iterate
        // the vector backwards, accessing only
        // every second element.
        let mut i = storage.v.len() - 1;
        while 0 <= i {
            log(storage.v.get(i).unwrap().read());
            i -= 2;
        }
    }
    // ANCHOR_END: storage_vec_iterate
    // ANCHOR: storage_vec_multiple_types_fn
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage
            .row
            .push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }
    // ANCHOR_END: storage_vec_multiple_types_fn

    // ANCHOR: access_nested_vec 
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }
    // ANCHOR_END: access_nested_vec 
}\n```

Note two details here. First, in order to use push, we need to first access the vector using the storage keyword. Second, because push requires accessing storage, a storage annotation is required on the ABI function that calls push. While it may seem that #[storage(write)] should be enough here, the read annotation is also required because each call to push requires reading (and then updating) the length of the StorageVec which is also stored in persistent storage.

Note The storage annotation is also required for any private function defined in the contract that tries to push into the vector.

Note There is no need to add the mut keyword when declaring a StorageVec<T>. All storage variables are mutable by default.

Reading Elements of Storage Vectors

To read a value stored in a vector at a particular index, you can use the get method as shown below:

```sway\ncontract;

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR_END: storage_vec_import
// ANCHOR: storage_vec_multiple_types_enum
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}
// ANCHOR_END: storage_vec_multiple_types_enum
storage {
    // ANCHOR: storage_vec_decl
    v: StorageVec<u64> = StorageVec {},
    // ANCHOR_END: storage_vec_decl
    // ANCHOR: storage_vec_multiple_types_decl
    row: StorageVec<TableCell> = StorageVec {},
    // ANCHOR_END: storage_vec_multiple_types_decl
    // ANCHOR: storage_vec_nested
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},
    // ANCHOR_END: storage_vec_nested
}

abi StorageVecContract {
    #[storage(read, write)]
    fn push_to_storage_vec();

    #[storage(read)]
    fn read_from_storage_vec();

    #[storage(read)]
    fn iterate_over_a_storage_vec();

    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec();

    #[storage(read, write)]
    fn access_nested_vec();
}

impl StorageVecContract for Contract {
    // ANCHOR: storage_vec_push
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }
    // ANCHOR_END: storage_vec_push
    // ANCHOR: storage_vec_get
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Some(third) => log(third.read()),
            None => revert(42),
        }
    }
    // ANCHOR_END: storage_vec_get
    // ANCHOR: storage_vec_iterate
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        // Iterate over all the elements
        // in turn using the `while` loop.
        // **This approach is not recommended.**
        // For iterating over all the elements
        // in turn use the `for` loop instead.
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }

        // The preferred and most performant way
        // to iterate over all the elements in turn is
        // to use the `for` loop.
        for elem in storage.v.iter() {
            log(elem.read());
        }

        // Use the `while` loop only when more
        // control over traversal is needed.
        // E.g., in the below example we iterate
        // the vector backwards, accessing only
        // every second element.
        let mut i = storage.v.len() - 1;
        while 0 <= i {
            log(storage.v.get(i).unwrap().read());
            i -= 2;
        }
    }
    // ANCHOR_END: storage_vec_iterate
    // ANCHOR: storage_vec_multiple_types_fn
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage
            .row
            .push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }
    // ANCHOR_END: storage_vec_multiple_types_fn

    // ANCHOR: access_nested_vec 
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }
    // ANCHOR_END: access_nested_vec 
}\n```

Note three details here. First, we use the index value of 2 to get the third element because vectors are indexed by number, starting at zero. Second, we get the third element by using the get method with the index passed as an argument, which gives us an Option<StorageKey<T>>. Third, the ABI function calling get only requires the annotation #[storage(read)] as one might expect because get does not write to storage.

When the get method is passed an index that is outside the vector, it returns None without panicking. This is particularly useful if accessing an element beyond the range of the vector may happen occasionally under normal circumstances. Your code will then have logic to handle having either Some(element) or None. For example, the index could be coming as a contract method argument. If the argument passed is too large, the method get will return a None value, and the contract method may then decide to revert when that happens or return a meaningful error that tells the user how many items are in the current vector and give them another chance to pass a valid value.

Iterating over the Values in a Vector

Iterating over a storage vector is conceptually the same as iterating over a Vec<T>. The only difference is an additional call to read() to actually read the stored value.

```sway\ncontract;

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR_END: storage_vec_import
// ANCHOR: storage_vec_multiple_types_enum
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}
// ANCHOR_END: storage_vec_multiple_types_enum
storage {
    // ANCHOR: storage_vec_decl
    v: StorageVec<u64> = StorageVec {},
    // ANCHOR_END: storage_vec_decl
    // ANCHOR: storage_vec_multiple_types_decl
    row: StorageVec<TableCell> = StorageVec {},
    // ANCHOR_END: storage_vec_multiple_types_decl
    // ANCHOR: storage_vec_nested
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},
    // ANCHOR_END: storage_vec_nested
}

abi StorageVecContract {
    #[storage(read, write)]
    fn push_to_storage_vec();

    #[storage(read)]
    fn read_from_storage_vec();

    #[storage(read)]
    fn iterate_over_a_storage_vec();

    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec();

    #[storage(read, write)]
    fn access_nested_vec();
}

impl StorageVecContract for Contract {
    // ANCHOR: storage_vec_push
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }
    // ANCHOR_END: storage_vec_push
    // ANCHOR: storage_vec_get
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Some(third) => log(third.read()),
            None => revert(42),
        }
    }
    // ANCHOR_END: storage_vec_get
    // ANCHOR: storage_vec_iterate
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        // Iterate over all the elements
        // in turn using the `while` loop.
        // **This approach is not recommended.**
        // For iterating over all the elements
        // in turn use the `for` loop instead.
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }

        // The preferred and most performant way
        // to iterate over all the elements in turn is
        // to use the `for` loop.
        for elem in storage.v.iter() {
            log(elem.read());
        }

        // Use the `while` loop only when more
        // control over traversal is needed.
        // E.g., in the below example we iterate
        // the vector backwards, accessing only
        // every second element.
        let mut i = storage.v.len() - 1;
        while 0 <= i {
            log(storage.v.get(i).unwrap().read());
            i -= 2;
        }
    }
    // ANCHOR_END: storage_vec_iterate
    // ANCHOR: storage_vec_multiple_types_fn
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage
            .row
            .push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }
    // ANCHOR_END: storage_vec_multiple_types_fn

    // ANCHOR: access_nested_vec 
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }
    // ANCHOR_END: access_nested_vec 
}\n```

Note that modifying a vector during iteration, by e.g. adding or removing elements, is a logical error and results in an undefined behavior:

Using an Enum to store Multiple Types

Storage vectors, just like Vec<T>, can only store values that are the same type. Similarly to what we did for Vec<T> in the section Using an Enum to store Multiple Types, we can define an enum whose variants will hold the different value types, and all the enum variants will be considered the same type: that of the enum. This is shown below:

```sway\ncontract;

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR_END: storage_vec_import
// ANCHOR: storage_vec_multiple_types_enum
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}
// ANCHOR_END: storage_vec_multiple_types_enum
storage {
    // ANCHOR: storage_vec_decl
    v: StorageVec<u64> = StorageVec {},
    // ANCHOR_END: storage_vec_decl
    // ANCHOR: storage_vec_multiple_types_decl
    row: StorageVec<TableCell> = StorageVec {},
    // ANCHOR_END: storage_vec_multiple_types_decl
    // ANCHOR: storage_vec_nested
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},
    // ANCHOR_END: storage_vec_nested
}

abi StorageVecContract {
    #[storage(read, write)]
    fn push_to_storage_vec();

    #[storage(read)]
    fn read_from_storage_vec();

    #[storage(read)]
    fn iterate_over_a_storage_vec();

    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec();

    #[storage(read, write)]
    fn access_nested_vec();
}

impl StorageVecContract for Contract {
    // ANCHOR: storage_vec_push
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }
    // ANCHOR_END: storage_vec_push
    // ANCHOR: storage_vec_get
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Some(third) => log(third.read()),
            None => revert(42),
        }
    }
    // ANCHOR_END: storage_vec_get
    // ANCHOR: storage_vec_iterate
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        // Iterate over all the elements
        // in turn using the `while` loop.
        // **This approach is not recommended.**
        // For iterating over all the elements
        // in turn use the `for` loop instead.
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }

        // The preferred and most performant way
        // to iterate over all the elements in turn is
        // to use the `for` loop.
        for elem in storage.v.iter() {
            log(elem.read());
        }

        // Use the `while` loop only when more
        // control over traversal is needed.
        // E.g., in the below example we iterate
        // the vector backwards, accessing only
        // every second element.
        let mut i = storage.v.len() - 1;
        while 0 <= i {
            log(storage.v.get(i).unwrap().read());
            i -= 2;
        }
    }
    // ANCHOR_END: storage_vec_iterate
    // ANCHOR: storage_vec_multiple_types_fn
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage
            .row
            .push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }
    // ANCHOR_END: storage_vec_multiple_types_fn

    // ANCHOR: access_nested_vec 
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }
    // ANCHOR_END: access_nested_vec 
}\n```

Then we can declare a StorageVec in a storage block to hold that enum and so, ultimately, holds different types:

```sway\ncontract;

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR_END: storage_vec_import
// ANCHOR: storage_vec_multiple_types_enum
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}
// ANCHOR_END: storage_vec_multiple_types_enum
storage {
    // ANCHOR: storage_vec_decl
    v: StorageVec<u64> = StorageVec {},
    // ANCHOR_END: storage_vec_decl
    // ANCHOR: storage_vec_multiple_types_decl
    row: StorageVec<TableCell> = StorageVec {},
    // ANCHOR_END: storage_vec_multiple_types_decl
    // ANCHOR: storage_vec_nested
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},
    // ANCHOR_END: storage_vec_nested
}

abi StorageVecContract {
    #[storage(read, write)]
    fn push_to_storage_vec();

    #[storage(read)]
    fn read_from_storage_vec();

    #[storage(read)]
    fn iterate_over_a_storage_vec();

    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec();

    #[storage(read, write)]
    fn access_nested_vec();
}

impl StorageVecContract for Contract {
    // ANCHOR: storage_vec_push
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }
    // ANCHOR_END: storage_vec_push
    // ANCHOR: storage_vec_get
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Some(third) => log(third.read()),
            None => revert(42),
        }
    }
    // ANCHOR_END: storage_vec_get
    // ANCHOR: storage_vec_iterate
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        // Iterate over all the elements
        // in turn using the `while` loop.
        // **This approach is not recommended.**
        // For iterating over all the elements
        // in turn use the `for` loop instead.
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }

        // The preferred and most performant way
        // to iterate over all the elements in turn is
        // to use the `for` loop.
        for elem in storage.v.iter() {
            log(elem.read());
        }

        // Use the `while` loop only when more
        // control over traversal is needed.
        // E.g., in the below example we iterate
        // the vector backwards, accessing only
        // every second element.
        let mut i = storage.v.len() - 1;
        while 0 <= i {
            log(storage.v.get(i).unwrap().read());
            i -= 2;
        }
    }
    // ANCHOR_END: storage_vec_iterate
    // ANCHOR: storage_vec_multiple_types_fn
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage
            .row
            .push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }
    // ANCHOR_END: storage_vec_multiple_types_fn

    // ANCHOR: access_nested_vec 
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }
    // ANCHOR_END: access_nested_vec 
}\n```

We can now push different enum variants to the StorageVec as follows:

```sway\ncontract;

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR_END: storage_vec_import
// ANCHOR: storage_vec_multiple_types_enum
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}
// ANCHOR_END: storage_vec_multiple_types_enum
storage {
    // ANCHOR: storage_vec_decl
    v: StorageVec<u64> = StorageVec {},
    // ANCHOR_END: storage_vec_decl
    // ANCHOR: storage_vec_multiple_types_decl
    row: StorageVec<TableCell> = StorageVec {},
    // ANCHOR_END: storage_vec_multiple_types_decl
    // ANCHOR: storage_vec_nested
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},
    // ANCHOR_END: storage_vec_nested
}

abi StorageVecContract {
    #[storage(read, write)]
    fn push_to_storage_vec();

    #[storage(read)]
    fn read_from_storage_vec();

    #[storage(read)]
    fn iterate_over_a_storage_vec();

    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec();

    #[storage(read, write)]
    fn access_nested_vec();
}

impl StorageVecContract for Contract {
    // ANCHOR: storage_vec_push
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }
    // ANCHOR_END: storage_vec_push
    // ANCHOR: storage_vec_get
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Some(third) => log(third.read()),
            None => revert(42),
        }
    }
    // ANCHOR_END: storage_vec_get
    // ANCHOR: storage_vec_iterate
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        // Iterate over all the elements
        // in turn using the `while` loop.
        // **This approach is not recommended.**
        // For iterating over all the elements
        // in turn use the `for` loop instead.
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }

        // The preferred and most performant way
        // to iterate over all the elements in turn is
        // to use the `for` loop.
        for elem in storage.v.iter() {
            log(elem.read());
        }

        // Use the `while` loop only when more
        // control over traversal is needed.
        // E.g., in the below example we iterate
        // the vector backwards, accessing only
        // every second element.
        let mut i = storage.v.len() - 1;
        while 0 <= i {
            log(storage.v.get(i).unwrap().read());
            i -= 2;
        }
    }
    // ANCHOR_END: storage_vec_iterate
    // ANCHOR: storage_vec_multiple_types_fn
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage
            .row
            .push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }
    // ANCHOR_END: storage_vec_multiple_types_fn

    // ANCHOR: access_nested_vec 
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }
    // ANCHOR_END: access_nested_vec 
}\n```

Now that we’ve discussed some of the most common ways to use storage vectors, be sure to review the API documentation for all the many useful methods defined on StorageVec<T> by the standard library. For now, these can be found in the source code for StorageVec<T>. For example, in addition to push, a pop method removes and returns the last element, a remove method removes and returns the element at some chosen index within the vector, an insert method inserts an element at some chosen index within the vector, etc.

Nested Storage Vectors

It is possible to nest storage vectors as follows:

```sway\ncontract;

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR_END: storage_vec_import
// ANCHOR: storage_vec_multiple_types_enum
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}
// ANCHOR_END: storage_vec_multiple_types_enum
storage {
    // ANCHOR: storage_vec_decl
    v: StorageVec<u64> = StorageVec {},
    // ANCHOR_END: storage_vec_decl
    // ANCHOR: storage_vec_multiple_types_decl
    row: StorageVec<TableCell> = StorageVec {},
    // ANCHOR_END: storage_vec_multiple_types_decl
    // ANCHOR: storage_vec_nested
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},
    // ANCHOR_END: storage_vec_nested
}

abi StorageVecContract {
    #[storage(read, write)]
    fn push_to_storage_vec();

    #[storage(read)]
    fn read_from_storage_vec();

    #[storage(read)]
    fn iterate_over_a_storage_vec();

    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec();

    #[storage(read, write)]
    fn access_nested_vec();
}

impl StorageVecContract for Contract {
    // ANCHOR: storage_vec_push
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }
    // ANCHOR_END: storage_vec_push
    // ANCHOR: storage_vec_get
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Some(third) => log(third.read()),
            None => revert(42),
        }
    }
    // ANCHOR_END: storage_vec_get
    // ANCHOR: storage_vec_iterate
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        // Iterate over all the elements
        // in turn using the `while` loop.
        // **This approach is not recommended.**
        // For iterating over all the elements
        // in turn use the `for` loop instead.
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }

        // The preferred and most performant way
        // to iterate over all the elements in turn is
        // to use the `for` loop.
        for elem in storage.v.iter() {
            log(elem.read());
        }

        // Use the `while` loop only when more
        // control over traversal is needed.
        // E.g., in the below example we iterate
        // the vector backwards, accessing only
        // every second element.
        let mut i = storage.v.len() - 1;
        while 0 <= i {
            log(storage.v.get(i).unwrap().read());
            i -= 2;
        }
    }
    // ANCHOR_END: storage_vec_iterate
    // ANCHOR: storage_vec_multiple_types_fn
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage
            .row
            .push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }
    // ANCHOR_END: storage_vec_multiple_types_fn

    // ANCHOR: access_nested_vec 
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }
    // ANCHOR_END: access_nested_vec 
}\n```

The nested vector can then be accessed as follows:

```sway\ncontract;

// ANCHOR: storage_vec_import
use std::storage::storage_vec::*;
// ANCHOR_END: storage_vec_import
// ANCHOR: storage_vec_multiple_types_enum
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}
// ANCHOR_END: storage_vec_multiple_types_enum
storage {
    // ANCHOR: storage_vec_decl
    v: StorageVec<u64> = StorageVec {},
    // ANCHOR_END: storage_vec_decl
    // ANCHOR: storage_vec_multiple_types_decl
    row: StorageVec<TableCell> = StorageVec {},
    // ANCHOR_END: storage_vec_multiple_types_decl
    // ANCHOR: storage_vec_nested
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},
    // ANCHOR_END: storage_vec_nested
}

abi StorageVecContract {
    #[storage(read, write)]
    fn push_to_storage_vec();

    #[storage(read)]
    fn read_from_storage_vec();

    #[storage(read)]
    fn iterate_over_a_storage_vec();

    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec();

    #[storage(read, write)]
    fn access_nested_vec();
}

impl StorageVecContract for Contract {
    // ANCHOR: storage_vec_push
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }
    // ANCHOR_END: storage_vec_push
    // ANCHOR: storage_vec_get
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Some(third) => log(third.read()),
            None => revert(42),
        }
    }
    // ANCHOR_END: storage_vec_get
    // ANCHOR: storage_vec_iterate
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        // Iterate over all the elements
        // in turn using the `while` loop.
        // **This approach is not recommended.**
        // For iterating over all the elements
        // in turn use the `for` loop instead.
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }

        // The preferred and most performant way
        // to iterate over all the elements in turn is
        // to use the `for` loop.
        for elem in storage.v.iter() {
            log(elem.read());
        }

        // Use the `while` loop only when more
        // control over traversal is needed.
        // E.g., in the below example we iterate
        // the vector backwards, accessing only
        // every second element.
        let mut i = storage.v.len() - 1;
        while 0 <= i {
            log(storage.v.get(i).unwrap().read());
            i -= 2;
        }
    }
    // ANCHOR_END: storage_vec_iterate
    // ANCHOR: storage_vec_multiple_types_fn
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage
            .row
            .push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }
    // ANCHOR_END: storage_vec_multiple_types_fn

    // ANCHOR: access_nested_vec 
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }
    // ANCHOR_END: access_nested_vec 
}\n```

Storage Maps

Another important common collection is the storage map.

The type StorageMap<K, V> from the standard library stores a mapping of keys of type K to values of type V using a hashing function, which determines how it places these keys and values into storage slots. This is similar to Rust's HashMap<K, V> but with a few differences.

Storage maps are useful when you want to look up data not by using an index, as you can with vectors, but by using a key that can be of any type. For example, when building a ledger-based sub-currency smart contract, you could keep track of the balance of each wallet in a storage map in which each key is a wallet’s Address and the values are each wallet’s balance. Given an Address, you can retrieve its balance.

Similarly to StorageVec<T>, StorageMap<K, V> can only be used in a contract because only contracts are allowed to access persistent storage.

StorageMap<T> is included in the standard library prelude which means that there is no need to import it manually.

Creating a New Storage Map

To create a new empty storage map, we have to declare the map in a storage block as follows:

```sway\ncontract;

use std::hash::*;

storage {
    // ANCHOR: storage_map_decl
    map: StorageMap<Address, u64> = StorageMap::<Address, u64> {},
    // ANCHOR_END: storage_map_decl
    // ANCHOR: storage_map_tuple_key
    map_two_keys: StorageMap<(b256, bool), b256> = StorageMap::<(b256, bool), b256> {},
    // ANCHOR_END: storage_map_tuple_key
    // ANCHOR: storage_map_nested
    nested_map: StorageMap<u64, StorageMap<u64, u64>> = StorageMap::<u64, StorageMap<u64, u64>> {},
    // ANCHOR_END: storage_map_nested
}

abi StorageMapExample {
    #[storage(write)]
    fn insert_into_storage_map();

    #[storage(read, write)]
    fn get_from_storage_map();

    #[storage(read, write)]
    fn access_nested_map();
}

impl StorageMapExample for Contract {
    // ANCHOR: storage_map_insert
    #[storage(write)]
    fn insert_into_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);
    }
    // ANCHOR_END: storage_map_insert
    // ANCHOR: storage_map_get
    #[storage(read, write)]
    fn get_from_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);

        let value1 = storage.map.get(addr1).try_read().unwrap_or(0);
    }
    // ANCHOR_END: storage_map_get

    // ANCHOR: storage_map_nested_access
    #[storage(read, write)]
    fn access_nested_map() {
        storage.nested_map.get(0).insert(1, 42);
        storage.nested_map.get(2).insert(3, 24);

        assert(storage.nested_map.get(0).get(1).read() == 42);
        assert(storage.nested_map.get(0).get(0).try_read().is_none()); // Nothing inserted here
        assert(storage.nested_map.get(2).get(3).read() == 24);
        assert(storage.nested_map.get(2).get(2).try_read().is_none()); // Nothing inserted here
    }
    // ANCHOR_END: storage_map_nested_access
}\n```

Just like any other storage variable, two things are required when declaring a StorageMap: a type annotation and an initializer. The initializer is just an empty struct of type StorageMap because StorageMap<K, V> itself is an empty struct! Everything that is interesting about StorageMap<K, V> is implemented in its methods.

Storage maps, just like Vec<T> and StorageVec<T>, are implemented using generics which means that the StorageMap<K, V> type provided by the standard library can map keys of any type K to values of any type V. In the example above, we’ve told the Sway compiler that the StorageMap<K, V> in map will map keys of type Address to values of type u64.

Updating a Storage Map

To insert key-value pairs into a storage map, we can use the insert method.

For example:

```sway\ncontract;

use std::hash::*;

storage {
    // ANCHOR: storage_map_decl
    map: StorageMap<Address, u64> = StorageMap::<Address, u64> {},
    // ANCHOR_END: storage_map_decl
    // ANCHOR: storage_map_tuple_key
    map_two_keys: StorageMap<(b256, bool), b256> = StorageMap::<(b256, bool), b256> {},
    // ANCHOR_END: storage_map_tuple_key
    // ANCHOR: storage_map_nested
    nested_map: StorageMap<u64, StorageMap<u64, u64>> = StorageMap::<u64, StorageMap<u64, u64>> {},
    // ANCHOR_END: storage_map_nested
}

abi StorageMapExample {
    #[storage(write)]
    fn insert_into_storage_map();

    #[storage(read, write)]
    fn get_from_storage_map();

    #[storage(read, write)]
    fn access_nested_map();
}

impl StorageMapExample for Contract {
    // ANCHOR: storage_map_insert
    #[storage(write)]
    fn insert_into_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);
    }
    // ANCHOR_END: storage_map_insert
    // ANCHOR: storage_map_get
    #[storage(read, write)]
    fn get_from_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);

        let value1 = storage.map.get(addr1).try_read().unwrap_or(0);
    }
    // ANCHOR_END: storage_map_get

    // ANCHOR: storage_map_nested_access
    #[storage(read, write)]
    fn access_nested_map() {
        storage.nested_map.get(0).insert(1, 42);
        storage.nested_map.get(2).insert(3, 24);

        assert(storage.nested_map.get(0).get(1).read() == 42);
        assert(storage.nested_map.get(0).get(0).try_read().is_none()); // Nothing inserted here
        assert(storage.nested_map.get(2).get(3).read() == 24);
        assert(storage.nested_map.get(2).get(2).try_read().is_none()); // Nothing inserted here
    }
    // ANCHOR_END: storage_map_nested_access
}\n```

Note two details here. First, in order to use insert, we need to first access the storage map using the storage keyword. Second, because insert requires writing into storage, a #[storage(write)] annotation is required on the ABI function that calls insert.

Note The storage annotation is also required for any private function defined in the contract that tries to insert into the map.

Note There is no need to add the mut keyword when declaring a StorageMap<K, V>. All storage variables are mutable by default.

Accessing Values in a Storage Map

We can get a value out of the storage map by providing its key to the get method.

For example:

```sway\ncontract;

use std::hash::*;

storage {
    // ANCHOR: storage_map_decl
    map: StorageMap<Address, u64> = StorageMap::<Address, u64> {},
    // ANCHOR_END: storage_map_decl
    // ANCHOR: storage_map_tuple_key
    map_two_keys: StorageMap<(b256, bool), b256> = StorageMap::<(b256, bool), b256> {},
    // ANCHOR_END: storage_map_tuple_key
    // ANCHOR: storage_map_nested
    nested_map: StorageMap<u64, StorageMap<u64, u64>> = StorageMap::<u64, StorageMap<u64, u64>> {},
    // ANCHOR_END: storage_map_nested
}

abi StorageMapExample {
    #[storage(write)]
    fn insert_into_storage_map();

    #[storage(read, write)]
    fn get_from_storage_map();

    #[storage(read, write)]
    fn access_nested_map();
}

impl StorageMapExample for Contract {
    // ANCHOR: storage_map_insert
    #[storage(write)]
    fn insert_into_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);
    }
    // ANCHOR_END: storage_map_insert
    // ANCHOR: storage_map_get
    #[storage(read, write)]
    fn get_from_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);

        let value1 = storage.map.get(addr1).try_read().unwrap_or(0);
    }
    // ANCHOR_END: storage_map_get

    // ANCHOR: storage_map_nested_access
    #[storage(read, write)]
    fn access_nested_map() {
        storage.nested_map.get(0).insert(1, 42);
        storage.nested_map.get(2).insert(3, 24);

        assert(storage.nested_map.get(0).get(1).read() == 42);
        assert(storage.nested_map.get(0).get(0).try_read().is_none()); // Nothing inserted here
        assert(storage.nested_map.get(2).get(3).read() == 24);
        assert(storage.nested_map.get(2).get(2).try_read().is_none()); // Nothing inserted here
    }
    // ANCHOR_END: storage_map_nested_access
}\n```

Here, value1 will have the value that's associated with the first address, and the result will be 42. The get method returns an Option<V>; if there’s no value for that key in the storage map, get will return None. This program handles the Option by calling unwrap_or to set value1 to zero if map doesn't have an entry for the key.

Storage Maps with Multiple Keys

Maps with multiple keys can be implemented using tuples as keys. For example:

```sway\ncontract;

use std::hash::*;

storage {
    // ANCHOR: storage_map_decl
    map: StorageMap<Address, u64> = StorageMap::<Address, u64> {},
    // ANCHOR_END: storage_map_decl
    // ANCHOR: storage_map_tuple_key
    map_two_keys: StorageMap<(b256, bool), b256> = StorageMap::<(b256, bool), b256> {},
    // ANCHOR_END: storage_map_tuple_key
    // ANCHOR: storage_map_nested
    nested_map: StorageMap<u64, StorageMap<u64, u64>> = StorageMap::<u64, StorageMap<u64, u64>> {},
    // ANCHOR_END: storage_map_nested
}

abi StorageMapExample {
    #[storage(write)]
    fn insert_into_storage_map();

    #[storage(read, write)]
    fn get_from_storage_map();

    #[storage(read, write)]
    fn access_nested_map();
}

impl StorageMapExample for Contract {
    // ANCHOR: storage_map_insert
    #[storage(write)]
    fn insert_into_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);
    }
    // ANCHOR_END: storage_map_insert
    // ANCHOR: storage_map_get
    #[storage(read, write)]
    fn get_from_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);

        let value1 = storage.map.get(addr1).try_read().unwrap_or(0);
    }
    // ANCHOR_END: storage_map_get

    // ANCHOR: storage_map_nested_access
    #[storage(read, write)]
    fn access_nested_map() {
        storage.nested_map.get(0).insert(1, 42);
        storage.nested_map.get(2).insert(3, 24);

        assert(storage.nested_map.get(0).get(1).read() == 42);
        assert(storage.nested_map.get(0).get(0).try_read().is_none()); // Nothing inserted here
        assert(storage.nested_map.get(2).get(3).read() == 24);
        assert(storage.nested_map.get(2).get(2).try_read().is_none()); // Nothing inserted here
    }
    // ANCHOR_END: storage_map_nested_access
}\n```

Nested Storage Maps

It is possible to nest storage maps as follows:

```sway\ncontract;

use std::hash::*;

storage {
    // ANCHOR: storage_map_decl
    map: StorageMap<Address, u64> = StorageMap::<Address, u64> {},
    // ANCHOR_END: storage_map_decl
    // ANCHOR: storage_map_tuple_key
    map_two_keys: StorageMap<(b256, bool), b256> = StorageMap::<(b256, bool), b256> {},
    // ANCHOR_END: storage_map_tuple_key
    // ANCHOR: storage_map_nested
    nested_map: StorageMap<u64, StorageMap<u64, u64>> = StorageMap::<u64, StorageMap<u64, u64>> {},
    // ANCHOR_END: storage_map_nested
}

abi StorageMapExample {
    #[storage(write)]
    fn insert_into_storage_map();

    #[storage(read, write)]
    fn get_from_storage_map();

    #[storage(read, write)]
    fn access_nested_map();
}

impl StorageMapExample for Contract {
    // ANCHOR: storage_map_insert
    #[storage(write)]
    fn insert_into_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);
    }
    // ANCHOR_END: storage_map_insert
    // ANCHOR: storage_map_get
    #[storage(read, write)]
    fn get_from_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);

        let value1 = storage.map.get(addr1).try_read().unwrap_or(0);
    }
    // ANCHOR_END: storage_map_get

    // ANCHOR: storage_map_nested_access
    #[storage(read, write)]
    fn access_nested_map() {
        storage.nested_map.get(0).insert(1, 42);
        storage.nested_map.get(2).insert(3, 24);

        assert(storage.nested_map.get(0).get(1).read() == 42);
        assert(storage.nested_map.get(0).get(0).try_read().is_none()); // Nothing inserted here
        assert(storage.nested_map.get(2).get(3).read() == 24);
        assert(storage.nested_map.get(2).get(2).try_read().is_none()); // Nothing inserted here
    }
    // ANCHOR_END: storage_map_nested_access
}\n```

The nested map can then be accessed as follows:

```sway\ncontract;

use std::hash::*;

storage {
    // ANCHOR: storage_map_decl
    map: StorageMap<Address, u64> = StorageMap::<Address, u64> {},
    // ANCHOR_END: storage_map_decl
    // ANCHOR: storage_map_tuple_key
    map_two_keys: StorageMap<(b256, bool), b256> = StorageMap::<(b256, bool), b256> {},
    // ANCHOR_END: storage_map_tuple_key
    // ANCHOR: storage_map_nested
    nested_map: StorageMap<u64, StorageMap<u64, u64>> = StorageMap::<u64, StorageMap<u64, u64>> {},
    // ANCHOR_END: storage_map_nested
}

abi StorageMapExample {
    #[storage(write)]
    fn insert_into_storage_map();

    #[storage(read, write)]
    fn get_from_storage_map();

    #[storage(read, write)]
    fn access_nested_map();
}

impl StorageMapExample for Contract {
    // ANCHOR: storage_map_insert
    #[storage(write)]
    fn insert_into_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);
    }
    // ANCHOR_END: storage_map_insert
    // ANCHOR: storage_map_get
    #[storage(read, write)]
    fn get_from_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);

        let value1 = storage.map.get(addr1).try_read().unwrap_or(0);
    }
    // ANCHOR_END: storage_map_get

    // ANCHOR: storage_map_nested_access
    #[storage(read, write)]
    fn access_nested_map() {
        storage.nested_map.get(0).insert(1, 42);
        storage.nested_map.get(2).insert(3, 24);

        assert(storage.nested_map.get(0).get(1).read() == 42);
        assert(storage.nested_map.get(0).get(0).try_read().is_none()); // Nothing inserted here
        assert(storage.nested_map.get(2).get(3).read() == 24);
        assert(storage.nested_map.get(2).get(2).try_read().is_none()); // Nothing inserted here
    }
    // ANCHOR_END: storage_map_nested_access
}\n```

Testing

Sway aims to provide facilities for both unit testing and integration testing.

Unit testing refers to "in-language" test functions annotated with #[test].

Integration testing refers to the testing of your Sway project's integration within some wider application. You can add integration testing to your Sway+Rust projects today using the cargo generate template and Rust SDK.

Unit Testing

Forc provides built-in support for building and executing tests for a package.

Tests are written as free functions with the #[test] attribute.

For example:

#[test]
fn test_meaning_of_life() {
    assert(6 * 7 == 42);
}

Each test function is ran as if it were the entry point for a script. Tests "pass" if they return successfully, and "fail" if they revert or vice versa while testing failure.

If the project has failing tests forc test will exit with exit status 101.

Building and Running Tests

We can build and execute all tests within a package with the following:

forc test

The output should look similar to this:

  Compiled library "std".
  Compiled library "lib_single_test".
  Bytecode size is 92 bytes.
   Running 1 tests
      test test_meaning_of_life ... ok (170.652µs)
   Result: OK. 1 passed. 0 failed. Finished in 1.564996ms.

Visit the forc test command reference to find the options available for forc test.

Testing Failure

Forc supports testing failing cases for test functions declared with #[test(should_revert)].

For example:

#[test(should_revert)]
fn test_meaning_of_life() {
    assert(6 * 6 == 42);
}

It is also possible to specify an expected revert code, like the following example.

#[test(should_revert = "18446744073709486084")]
fn test_meaning_of_life() {
    assert(6 * 6 == 42);
}

Tests with #[test(should_revert)] are considered to be passing if they are reverting.

Calling Contracts

Unit tests can call contract functions an example for such calls can be seen below.

contract;

abi MyContract {
    fn test_function() -> bool;
}

impl MyContract for Contract {
    fn test_function() -> bool {
        true
    }
}

To test the test_function(), a unit test like the following can be written.

#[test]
fn test_success() {
    let caller = abi(MyContract, CONTRACT_ID);
    let result = caller.test_function {}();
    assert(result == true)
}

It is also possible to test failure with contract calls as well.

#[test(should_revert)]
fn test_fail() {
    let caller = abi(MyContract, CONTRACT_ID);
    let result = caller.test_function {}();
    assert(result == false)
}

Note: When running forc test, your contract will be built twice: first without unit tests in order to determine the contract's ID, then a second time with unit tests with the CONTRACT_ID provided to their namespace. This CONTRACT_ID can be used with the abi cast to enable contract calls within unit tests.

Unit tests can call methods of external contracts if those contracts are added as contract dependencies, i.e. in the contract-dependencies section of the manifest file. An example of such calls is shown below:

```sway\n// ANCHOR: multi_contract_calls 
contract;

abi CallerContract {
    fn test_false() -> bool;
}

impl CallerContract for Contract {
    fn test_false() -> bool {
        false
    }
}

abi CalleeContract {
    fn test_true() -> bool;
}

#[test]
fn test_multi_contract_calls() {
    let caller = abi(CallerContract, CONTRACT_ID);
    let callee = abi(CalleeContract, callee::CONTRACT_ID);

    let should_be_false = caller.test_false();
    let should_be_true = callee.test_true();
    assert(!should_be_false);
    assert(should_be_true);
}
// ANCHOR: multi_contract_calls\n```

Example Forc.toml for contract above:

```text\n# ANCHOR: multi_contract_call_toml
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "caller"

[dependencies]
std = { path = "../../../sway-lib-std/" }

[contract-dependencies]
callee = { path = "../callee" }
# ANCHOR: multi_contract_call_toml\n```

Running Tests in Parallel or Serially

By default, all unit tests in your project are run in parallel. Note that this does not lead to any data races in storage because each unit test has its own storage space that is not shared by any other unit test.

By default, forc test will use all the available threads in your system. To request that a specific number of threads be used, the flag --test-threads <val> can be provided to forc test.

forc test --test-threads 1

Logs Inside Tests

Forc has some capacity to help decode logs returned from the unit tests. You can use this feature to decode raw logs into a human readable format.

script;

fn main() {}

#[test]
fn test_fn() {
let a = 10;
    log(a);
    let b = 30;
    log(b);
    assert_eq(a, 10)
    assert_eq(b, 30)
}

The example shown above is logging two different variables, a and b and their values are 10 and 30, respectively. Without log decoding printed log for this test with forc test --logs (--logs flag is required to see the logs for this example since the test is passing. Logs are silenced by default in passing tests, and can be enabled using the --logs flag.):

Finished debug [unoptimized + fuel] target(s) in 5.23s
      Bytecode hash: 0x1cb1edc031691c5c08b50fd0f07b02431848ab81b325b72eb3fd233c67d6b548
   Running 1 test, filtered 0 tests
      test test_fn ... ok (38.875µs, 232 gas)
[{"LogData":{"data":"000000000000000a","digest":"8d85f8467240628a94819b26bee26e3a9b2804334c63482deacec8d64ab4e1e7","id":"0000000000000000000000000000000000000000000000000000000000000000","is":10368,"len":8,"pc":11032,"ptr":67107840,"ra":0,"rb":0}},{"LogData":{"data":"000000000000001e","digest":"48a97e421546f8d4cae1cf88c51a459a8c10a88442eed63643dd263cef880c1c","id":"0000000000000000000000000000000000000000000000000000000000000000","is":10368,"len":8,"pc":11516,"ptr":67106816,"ra":0,"rb":1}}]

This is not very easy to understand, it is possible to decode these logs with --decode flag, executing forc test --logs --decode:

Finished debug [unoptimized + fuel] target(s) in 5.23s
      Bytecode hash: 0x1cb1edc031691c5c08b50fd0f07b02431848ab81b325b72eb3fd233c67d6b548
   Running 1 test, filtered 0 tests
      test test_fn ... ok (38.875µs, 232 gas)
Decoded log value: 10, log rb: 0
Decoded log value: 30, log rb: 1

As it can be seen, the values are human readable and easier to understand which makes debugging much more easier.

Note: This is an experimental feature and we are actively working on reporting variable names next to their values.

Testing with Rust

A common use of Sway is for writing contracts or scripts that exist as part of a wider Rust application. In order to test the interaction between our Sway code and our Rust code we can add integration testing.

Adding Rust Integration Testing

To add Rust integration testing to a Forc project we can use the sway-test-rs cargo generate template. This template makes it easier for Sway developers to add the boilerplate required when setting up their Rust integration testing.

Let's add a Rust integration test to the fresh project we created in the introduction.

1. Enter the project

To recap, here's what our empty project looks like:

$ cd my-fuel-project
$ tree .
├── Forc.toml
└── src
    └── main.sw

2. Install cargo generate

We're going to add a Rust integration test harness using a cargo generate template. Let's make sure we have the cargo generate command installed!

cargo install cargo-generate

Note: You can learn more about cargo generate by visiting the cargo-generate repository.

3. Generate the test harness

Let's generate the default test harness with the following:

cargo generate --init fuellabs/sway templates/sway-test-rs --name my-fuel-project --force

--force forces your --name input to retain your desired casing for the {{project-name}} placeholder in the template. Otherwise, cargo-generate automatically converts it to kebab-case. With --force, this means that both my_fuel_project and my-fuel-project are valid project names, depending on your needs.

_Note: templates/sway-test-rs can be replaced with templates/sway-script-test-rs or templates/sway-predicate-test-rs to generate a test harness for scripts and predicates respectively.

If all goes well, the output should look as follows:

⚠️   Favorite `fuellabs/sway` not found in config, using it as a git repository: https://github.com/fuellabs/sway
🤷   Project Name : my-fuel-project
🔧   Destination: /home/user/path/to/my-fuel-project ...
🔧   Generating template ...
[1/3]   Done: Cargo.toml
[2/3]   Done: tests/harness.rs
[3/3]   Done: tests
🔧   Moving generated files into: `/home/user/path/to/my-fuel-project`...
✨   Done! New project created /home/user/path/to/my-fuel-project

Let's have a look at the result:

$ tree .
├── Cargo.toml
├── Forc.toml
├── build.rs
├── src
│   └── main.sw
└── tests
    └── harness.rs

We have three new files!

  • The Cargo.toml is the manifest for our new test harness and specifies the required dependencies including fuels the Fuel Rust SDK.
  • The tests/harness.rs contains some boilerplate test code to get us started, though doesn't call any contract methods just yet.
  • The build.rs is a build script that compiles the Sway project with forc build whenever cargo test is run.

4. Build the forc project

Before running the tests, we need to build our contract so that the necessary ABI, storage and bytecode artifacts are available. We can do so with forc build:

$ forc build
  Creating a new `Forc.lock` file. (Cause: lock file did not exist)
    Adding std git+https://github.com/fuellabs/sway?tag=v0.24.5#e695606d8884a18664f6231681333a784e623bc9
   Created new lock file at /home/user/path/to/my-fuel-project/Forc.lock
  Compiled library "std".
  Compiled contract "my-fuel-project".
  Bytecode size is 60 bytes.

At this point, our project should look like the following:

$ tree
├── Cargo.toml
├── Forc.lock
├── Forc.toml
├── build.rs
├── out
│   └── debug
│       ├── my-fuel-project-abi.json
│       ├── my-fuel-project.bin
│       └── my-fuel-project-storage_slots.json
├── src
│   └── main.sw
└── tests
    └── harness.rs

We now have an out directory with our required JSON files!

Note: This step may no longer be required in the future as we plan to enable the integration testing to automatically build the artifacts as necessary so that files like the ABI JSON are always up to date.

5. Build and run the tests

Now we're ready to build and run the default integration test.

$ cargo test
    Updating crates.io index
   Compiling version_check v0.9.4
   Compiling proc-macro2 v1.0.46
   Compiling quote v1.0.21
   ...
   Compiling fuels v0.24.0
   Compiling my-fuel-project v0.1.0 (/home/user/path/to/my-fuel-project)
    Finished test [unoptimized + debuginfo] target(s) in 1m 03s
     Running tests/harness.rs (target/debug/deps/integration_tests-373971ac377845f7)

running 1 test
test can_get_contract_id ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.36s

Note: The first time we run cargo test, cargo will spend some time fetching and building the dependencies for Fuel's Rust SDK. This might take a while, but only the first time!

If all went well, we should see some output that looks like the above!

Writing Tests

Now that we've learned how to setup Rust integration testing in our project, let's try to write some of our own tests!

First, let's update our contract code with a simple counter example:

```sway\ncontract;

abi TestContract {
    #[storage(write)]
    fn initialize_counter(value: u64) -> u64;

    #[storage(read, write)]
    fn increment_counter(amount: u64) -> u64;
}

storage {
    counter: u64 = 0,
}

impl TestContract for Contract {
    #[storage(write)]
    fn initialize_counter(value: u64) -> u64 {
        storage.counter.write(value);
        value
    }

    #[storage(read, write)]
    fn increment_counter(amount: u64) -> u64 {
        let incremented = storage.counter.read() + amount;
        storage.counter.write(incremented);
        incremented
    }
}\n```

To test our initialize_counter and increment_counter contract methods from the Rust test harness, we could update our tests/harness.rs file with the following:

use fuels::{prelude::*, types::ContractId};

// Load abi from json
abigen!(Contract(
    name = "MyContract",
    abi = "out/debug/my-fuel-project-abi.json"
));

async fn get_contract_instance() -> (MyContract<WalletUnlocked>, ContractId) {
    // Launch a local network and deploy the contract
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(
            Some(1),             /* Single wallet */
            Some(1),             /* Single coin (UTXO) */
            Some(1_000_000_000), /* Amount per coin */
        ),
        None,
        None,
    )
    .await
    .unwrap();
    let wallet = wallets.pop().unwrap();

    let id = Contract::load_from(
        "./out/debug/my-fuel-project.bin",
        LoadConfiguration::default().set_storage_configuration(
            StorageConfiguration::load_from(
                "./out/debug/my-fuel-project-storage_slots.json",
            )
            .unwrap(),
        ),
    )
    .unwrap()
    .deploy(&wallet, TxPolicies::default())
    .await
    .unwrap();

    let instance = MyContract::new(id.clone(), wallet);

    (instance, id.into())
}

#[tokio::test]
async fn initialize_and_increment() {
    let (contract_instance, _id) = get_contract_instance().await;
    // Now you have an instance of your contract you can use to test each function

    let result = contract_instance
        .methods()
        .initialize_counter(42)
        .call()
        .await
        .unwrap();

    assert_eq!(42, result.value);

    // Call `increment_counter()` method in our deployed contract.
    let result = contract_instance
        .methods()
        .increment_counter(10)
        .call()
        .await
        .unwrap();

    assert_eq!(52, result.value);
}

Let's build our project once more and run the test:

forc build
$ cargo test
   Compiling my-fuel-project v0.1.0 (/home/mindtree/programming/sway/my-fuel-project)
    Finished test [unoptimized + debuginfo] target(s) in 11.61s
     Running tests/harness.rs (target/debug/deps/integration_tests-373971ac377845f7)

running 1 test
test initialize_and_increment ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.25s

When cargo runs our test, our test uses the SDK to spin up a local in-memory Fuel network, deploy our contract to it, and call the contract methods via the ABI.

You can add as many functions decorated with #[tokio::test] as you like, and cargo test will automatically test each of them!

Debugging

Forc provides tools for debugging both live transactions as well as Sway unit tests. Debugging can be done via CLI or using the VSCode IDE.

Unit testing refers to "in-language" test functions annotated with #[test]. Line-by-line debugging is available within the VSCode IDE.

Live transaction refers to the testing sending a transaction to a running Fuel Client node to exercise your Sway code. Instruction-by-instruction debugging is available in the forc debug CLI.

Debugging with CLI

The forc debug CLI enables debugging a live transaction on a running Fuel Client node.

An example project

First, we need a project to debug, so create a new project using

forc new --script dbg_example && cd dbg_example

And then add some content to src/main.sw, for example:

script;

use std::logging::log;

fn factorial(n: u64) -> u64 {
    let mut result = 1;
    let mut counter = 0;
    while counter < n {
        counter = counter + 1;
        result = result * counter;
    }
    return result;
}

fn main() {
    log::<u64>(factorial(5)); // 120
}

Building and bytecode output

Now we are ready to build the project.

forc build

After this the resulting binary should be located at out/debug/dbg_example.bin. Because we are interested in the resulting bytecode, we can read that with:

forc parse-bytecode out/debug/dbg_example.bin

We can recognize the main loop by observing the control flow. Looking around halfword 58-60, we can see:

  half-word   byte    op                                                 raw
          58   232    MOVI { dst: 0x11, val: 5 }                         72 44 00 05                                 
          59   236    LT { dst: 0x10, lhs: 0x10, rhs: 0x11 }             16 41 04 40                                 
          60   240    JNZF { cond_nz: 0x10, dynamic: 0x0, fixed: 81 }    76 40 00 51

Here we can see our factorial(5) being set up with MOVI setting the value 5, followed by the LT comparison and conditional jump JNZF. The multiplication for our factorial happens at halfword 147 with MUL { dst: 0x10, lhs: 0x10, rhs: 0x11 }. Finally, we can spot our log statement at halfword 139 with the LOGD instruction.

Setting up the debugging

We can start up the debug infrastructure. On a new terminal session run fuel-core run --db-type in-memory --debug; we need to have that running because it actually executes the program. Now we can fire up the debugger itself: forc-debug. Now if everything is set up correctly, you should see the debugger prompt (>>). You can use help command to list available commands.

The debugger supports tab completion to help you discover files in your current working directory (and its subdirectories):

  • Type tx and press tab to recursively search for valid transaction JSON files
  • After selecting a transaction file, press tab again to search for ABI files
  • You can keep pressing tab to cycle through the found files
  • Of course, you can also manually type the full path to any transaction or ABI file, they don't have to be in your current directory

Now we would like to inspect the program while it's running. To do this, we first need to send the script to the executor, i.e. fuel-core. To do so, we need a transaction specification, tx.json. It looks something like this:

{
  "Script": {
    "body": {
      "script_gas_limit": 1000000,
      "script": [
        26, 240, 48, 0, 116, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 96, 93, 255, 192, 1, 16, 255, 255, 0, 26, 236, 80, 0, 145, 0, 0, 184, 80, 67, 176, 80, 32, 248, 51, 0, 88, 251, 224, 2, 80, 251, 224, 4, 116, 0, 0, 37, 80, 71, 176, 40, 26, 233, 16, 0, 32, 248, 51, 0, 88, 251, 224, 2, 80, 251, 224, 4, 116, 0, 0, 136, 26, 71, 208, 0, 114, 72, 0, 24, 40, 237, 20, 128, 80, 79, 176, 120, 114, 68, 0, 24, 40, 79, 180, 64, 80, 71, 176, 160, 114, 72, 0, 24, 40, 69, 52, 128, 80, 71, 176, 96, 114, 72, 0, 24, 40, 69, 52, 128, 80, 75, 176, 64, 26, 233, 16, 0, 26, 229, 32, 0, 32, 248, 51, 0, 88, 251, 224, 2, 80, 251, 224, 4, 116, 0, 0, 144, 26, 71, 208, 0, 80, 75, 176, 24, 114, 76, 0, 16, 40, 73, 20, 192, 80, 71, 176, 144, 114, 76, 0, 16, 40, 69, 36, 192, 114, 72, 0, 16, 40, 65, 20, 128, 93, 69, 0, 1, 93, 65, 0, 0, 37, 65, 16, 0, 149, 0, 0, 63, 150, 8, 0, 0, 26, 236, 80, 0, 145, 0, 1, 88, 26, 87, 224, 0, 95, 236, 16, 42, 95, 236, 0, 41, 93, 67, 176, 41, 114, 68, 0, 5, 22, 65, 4, 64, 118, 64, 0, 81, 93, 67, 176, 42, 80, 71, 176, 200, 26, 233, 16, 0, 32, 248, 51, 0, 88, 251, 224, 2, 80, 251, 224, 4, 116, 0, 0, 87, 26, 71, 208, 0, 114, 72, 0, 24, 40, 237, 20, 128, 80, 71, 176, 160, 114, 72, 0, 24, 40, 71, 180, 128, 80, 75, 176, 24, 114, 76, 0, 24, 40, 73, 20, 192, 80, 71, 176, 88, 114, 76, 0, 24, 40, 69, 36, 192, 93, 83, 176, 11, 93, 79, 176, 12, 93, 71, 176, 13, 114, 72, 0, 8, 16, 73, 20, 128, 21, 73, 36, 192, 118, 72, 0, 1, 116, 0, 0, 7, 114, 72, 0, 2, 27, 73, 52, 128, 114, 76, 0, 8, 16, 77, 36, 192, 38, 76, 0, 0, 40, 29, 68, 64, 26, 80, 112, 0, 16, 73, 68, 64, 95, 73, 0, 0, 114, 64, 0, 8, 16, 65, 20, 0, 80, 71, 176, 112, 95, 237, 64, 14, 95, 237, 48, 15, 95, 237, 0, 16, 80, 67, 176, 48, 114, 72, 0, 24, 40, 65, 20, 128, 80, 71, 176, 136, 114, 72, 0, 24, 40, 69, 4, 128, 80, 67, 177, 8, 114, 72, 0, 24, 40, 65, 20, 128, 80, 71, 177, 48, 114, 72, 0, 24, 40, 69, 4, 128, 80, 67, 177, 48, 80, 71, 176, 240, 114, 72, 0, 24, 40, 69, 4, 128, 80, 67, 176, 224, 26, 233, 16, 0, 26, 229, 0, 0, 32, 248, 51, 0, 88, 251, 224, 2, 80, 251, 224, 4, 116, 0, 0, 56, 26, 67, 208, 0, 80, 71, 176, 72, 114, 72, 0, 16, 40, 69, 4, 128, 80, 67, 177, 32, 114, 72, 0, 16, 40, 65, 20, 128, 80, 71, 176, 184, 114, 72, 0, 16, 40, 69, 4, 128, 93, 67, 240, 0, 93, 71, 176, 23, 93, 75, 176, 24, 52, 1, 4, 82, 26, 244, 0, 0, 116, 0, 0, 8, 93, 67, 176, 41, 16, 65, 0, 64, 95, 237, 0, 41, 93, 67, 176, 42, 93, 71, 176, 41, 27, 65, 4, 64, 95, 237, 0, 42, 117, 0, 0, 91, 146, 0, 1, 88, 26, 249, 80, 0, 152, 8, 0, 0, 151, 0, 0, 63, 74, 248, 0, 0, 149, 0, 0, 15, 150, 8, 0, 0, 26, 236, 80, 0, 145, 0, 0, 72, 26, 67, 160, 0, 26, 71, 224, 0, 114, 72, 4, 0, 38, 72, 0, 0, 26, 72, 112, 0, 80, 79, 176, 24, 95, 237, 32, 3, 114, 72, 4, 0, 95, 237, 32, 4, 95, 236, 0, 5, 114, 72, 0, 24, 40, 237, 52, 128, 80, 75, 176, 48, 114, 76, 0, 24, 40, 75, 180, 192, 114, 76, 0, 24, 40, 65, 36, 192, 26, 245, 0, 0, 146, 0, 0, 72, 26, 249, 16, 0, 152, 8, 0, 0, 151, 0, 0, 15, 74, 248, 0, 0, 149, 0, 0, 63, 150, 8, 0, 0, 26, 236, 80, 0, 145, 0, 0, 104, 26, 67, 160, 0, 26, 71, 144, 0, 26, 75, 224, 0, 80, 79, 176, 80, 114, 80, 0, 24, 40, 77, 5, 0, 114, 64, 0, 24, 40, 237, 52, 0, 80, 67, 176, 40, 114, 76, 0, 24, 40, 67, 180, 192, 93, 79, 176, 5, 80, 65, 0, 16, 80, 83, 176, 64, 95, 237, 48, 8, 80, 77, 64, 8, 114, 84, 0, 8, 40, 77, 5, 64, 80, 67, 176, 24, 114, 76, 0, 16, 40, 65, 68, 192, 114, 76, 0, 16, 40, 69, 4, 192, 26, 245, 16, 0, 146, 0, 0, 104, 26, 249, 32, 0, 152, 8, 0, 0, 151, 0, 0, 63, 74, 248, 0, 0, 71, 0, 0, 0, 21, 6, 230, 244, 76, 29, 98, 145
      ],
      "script_data": [],
      "receipts_root": "0000000000000000000000000000000000000000000000000000000000000000"
    },
    "policies": {
      "bits": "MaxFee",
      "values": [0, 0, 0, 0]
    },
    "inputs": [
      {
        "CoinSigned": {
          "utxo_id": {
            "tx_id": "c49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2",
            "output_index": 18
          },
          "owner": "f1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e",
          "amount": 10599410012256088000,
          "asset_id": "2cafad611543e0265d89f1c2b60d9ebf5d56ad7e23d9827d6b522fd4d6e44bc3",
          "tx_pointer": {
            "block_height": 0,
            "tx_index": 0
          },
          "witness_index": 0,
          "maturity": 0,
          "predicate_gas_used": null,
          "predicate": null,
          "predicate_data": null
        }
      }
    ],
    "outputs": [],
    "witnesses": [
      {
        "data": [156, 254, 34, 102, 65, 96, 133, 170, 254, 105, 147, 35, 196, 199, 179, 133, 132, 240, 208, 149, 11, 46, 30, 96, 44, 91, 121, 195, 145, 184, 159, 235, 117, 82, 135, 41, 84, 154, 102, 61, 61, 16, 99, 123, 58, 173, 75, 226, 219, 139, 62, 33, 41, 176, 16, 18, 132, 178, 8, 125, 130, 169, 32, 108]
      }
    ]
  }
}

However, the key script should contain the actual bytecode to execute, i.e. the contents of out/debug/dbg_example.bin as a JSON array. The following command can be used to generate it:

python3 -c 'print(list(open("out/debug/dbg_example.bin", "rb").read()))'

So now we replace the script array with the result, and save it as tx.json.

Using the debugger

Now we can actually execute the script with an ABI to decode the log values:

>> start_tx tx.json out/debug/dbg_example-abi.json

Receipt: LogData { id: 0000000000000000000000000000000000000000000000000000000000000000, ra: 0, rb: 1515152261580153489, ptr: 67107840, len: 8, digest: d2b80ebb9ce633ad49a9ccfcc58ac7ad33a9ab4741529ae4247a3b07e8fa1c74, pc: 10924, is: 10368, data: Some(0000000000000078) }
Decoded log value: 120, from contract: 0000000000000000000000000000000000000000000000000000000000000000
Receipt: ReturnData { id: 0000000000000000000000000000000000000000000000000000000000000000, ptr: 67106816, len: 0, digest: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855, pc: 10564, is: 10368, data: Some() }
Receipt: ScriptResult { result: Success, gas_used: 1273 }
Terminated

Looking at the output, we can see our factorial(5) result as the decoded log value of 120. The ABI has helped us decode the raw bytes (0000000000000078) into a meaningful value. It also tells us that the execution terminated without hitting any breakpoints. That's unsurprising, because we haven't set up any. We can do so with breakpoint command:

>> breakpoint 0

>> start_tx tx.json out/debug/dbg_example-abi.json

Receipt: ScriptResult { result: Success, gas_used: 0 }
Stopped on breakpoint at address 0 of contract 0x0000000000000000000000000000000000000000000000000000000000000000

Now we have stopped execution at the breakpoint on entry (address 0). We can now inspect the initial state of the VM.

>> register ggas

reg[0x9] = 1000000  # ggas

>> memory 0x10 0x8

 000010: db f3 63 c9 1c 7f ec 95

However, that's not too interesting either, so let's just execute until the end, and then reset the VM to remove the breakpoints.

>> continue

Receipt: LogData { id: 0000000000000000000000000000000000000000000000000000000000000000, ra: 0, rb: 1515152261580153489, ptr: 67107840, len: 8, digest: d2b80ebb9ce633ad49a9ccfcc58ac7ad33a9ab4741529ae4247a3b07e8fa1c74, pc: 10924, is: 10368, data: Some(0000000000000078) }
Decoded log value: 120, from contract: 0000000000000000000000000000000000000000000000000000000000000000
Receipt: ReturnData { id: 0000000000000000000000000000000000000000000000000000000000000000, ptr: 67106816, len: 0, digest: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855, pc: 10564, is: 10368, data: Some() }
Terminated

>> reset

Next, we will setup a breakpoint to check the state on each iteration of the while loop. For instance, if we'd like to see what numbers get multiplied together, we could set up a breakpoint before the operation. Looking at our bytecode we can see the main multiplication for our factorial happens at:

  half-word   byte   op                                        raw
        147   588    MUL { dst: 0x10, lhs: 0x10, rhs: 0x11 }   1b 41 04 40

We can set a breakpoint on its address, at halfword-offset 147.

>>> breakpoint 147

>> start_tx tx.json out/debug/dbg_example-abi.json

Receipt: ScriptResult { result: Success, gas_used: 82 }
Stopped on breakpoint at address 588 of contract 0x0000000000000000000000000000000000000000000000000000000000000000

Now we can inspect the inputs to multiply. Looking at the specification tells us that the instruction MUL { dst: 0x10, lhs: 0x10, rhs: 0x11 } means reg[0x10] = reg[0x10] * reg[0x11]. So inspecting the inputs:

>> r 0x10 0x11
reg[0x10] = 1        # reg16
reg[0x11] = 1        # reg17

So on the first round the numbers are 1 and 1, so we can continue to the next iteration with the c command:

>> c
Stopped on breakpoint at address 588 of contract 0x0000000000000000000000000000000000000000000000000000000000000000

>> r 0x10 0x11
reg[0x10] = 1        # reg16
reg[0x11] = 2        # reg17

And the next one:

>> c
Stopped on breakpoint at address 588 of contract 0x0000000000000000000000000000000000000000000000000000000000000000

>> r 0x10 0x11
reg[0x10] = 2        # reg16
reg[0x11] = 3        # reg17

And fourth one:

>> c
Stopped on breakpoint at address 588 of contract 0x0000000000000000000000000000000000000000000000000000000000000000

>> r 0x10 0x11
reg[0x10] = 6        # reg16
reg[0x11] = 4        # reg17

And round 5:

>> c
Stopped on breakpoint at address 588 of contract 0x0000000000000000000000000000000000000000000000000000000000000000

>> r 0x10 0x11
reg[0x10] = 24       # reg16
reg[0x11] = 5        # reg17

At this point we can look at the values

0x100x11
11
12
23
64
245

From this we can clearly see that the left side, register 0x10 is the result variable which accumulates the factorial calculation (1, 1, 2, 6, 24), and register 0x11 is the counter which increments from 1 to 5. Now the counter equals the given factorial function argument 5, and the loop terminates. So when we continue, the program finishes without encountering any more breakpoints:

>> c

Receipt: LogData { id: 0000000000000000000000000000000000000000000000000000000000000000, ra: 0, rb: 1515152261580153489, ptr: 67107840, len: 8, digest: d2b80ebb9ce633ad49a9ccfcc58ac7ad33a9ab4741529ae4247a3b07e8fa1c74, pc: 10924, is: 10368, data: Some(0000000000000078) }
Decoded log value: 120, from contract: 0000000000000000000000000000000000000000000000000000000000000000
Receipt: ReturnData { id: 0000000000000000000000000000000000000000000000000000000000000000, ptr: 67106816, len: 0, digest: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855, pc: 10564, is: 10368, data: Some() }
Terminated

Debugging with IDE

The forc debug plugin also enables line-by-line debugging of Sway unit tests in VSCode.

Installation

  1. Install the Sway VSCode extension from the marketplace.
  2. Ensure you have the forc-debug binary installed. which forc-debug. It can be installed with fuelup component add forc-debug.
  3. Create a .vscode/launch.json file with the following contents:
{
    "version": "0.2.0",
    "configurations": [
        {
        "type": "sway",
        "request": "launch",
        "name": "Debug Sway",
        "program": "${file}"
    }]
}

An example project

Given this example contract:

contract;

abi CallerContract {
    fn test_false() -> bool;
}

impl CallerContract for Contract {
    fn test_false() -> bool {
        false
    }
}

abi CalleeContract {
    fn test_true() -> bool;
}

#[test]
fn test_multi_contract_calls() {
    let caller = abi(CallerContract, CONTRACT_ID);
    let callee = abi(CalleeContract, callee::CONTRACT_ID);

    let should_be_false = caller.test_false();
    let should_be_true = callee.test_true();
    assert(!should_be_false);
    assert(should_be_true);
}

Within the sway file open in VSCode, you can set breakpoints on lines within the test or functions that it calls, and click Run -> Start Debugging to begin debugging the unit test.

This will build the sway project and run it in debug mode. The debugger will stop the VM execution when a breakpoint is hit.

The debug panel will show VM registers under the Variables tab, as well as the current VM opcode where execution is suspended. You can continue execution, or use the Step Over function to step forward, instruction by instruction.

Sway LSP

Welcome to the documentation for Sway LSP, the language server designed specifically for the Sway programming language. This documentation serves as a comprehensive guide to help you understand and utilize the powerful features provided by Sway LSP.

Sway LSP is built on the Language Server Protocol (LSP), a standardized protocol for enabling rich programming language support in editor and IDE environments. It acts as a bridge between your favorite code editor or integrated development environment and the Sway programming language, offering advanced semantic analysis and a wide range of features to enhance your development experience.

With Sway LSP, you can expect a seamless and efficient coding experience while working with the Sway programming language. It provides intelligent code completion, precise symbol navigation, type information, and other smart features that empower you to write clean and error-free code. By leveraging the power of Sway LSP, you can increase productivity, reduce debugging time, and write high-quality code with confidence.

In this documentation, you will find detailed information about how to set up Sway LSP in your preferred code editor or IDE, configure its settings to match your coding style, and take advantage of its various features. We will guide you through the installation process, provide examples of typical configuration setups, and walk you through the usage of each feature supported by Sway LSP.

Whether you are a beginner or an experienced Sway developer, this documentation aims to be your go-to resource for understanding and maximizing the capabilities of Sway LSP. So let's dive in and unlock the full potential of the Sway programming language with Sway LSP!

Installation

The Sway language server is contained in the forc-lsp binary, which is installed as part of the Fuel toolchain. Once installed, it can be used with a variety of IDEs. It must be installed for any of the IDE plugins to work.

Note: There is no need to manually run forc-lsp (the plugin will automatically start it), however both forc and forc-lsp must be in your $PATH. To check if forc is in your $PATH, type forc --help in your terminal.

VSCode

This is the best supported editor at the moment.

You can install the latest release of the plugin from the marketplace.

Note that we only support the most recent version of VS Code.

Code OSS (VSCode on Linux)

  1. Install code-marketplace to get access to all of the extensions in the VSCode marketplace.
  2. Install the Sway extension.

vim / neovim

Follow the documentation for sway.vim to install.

helix

Install helix and Sway LSP will work out of the box.

Sway support is built into helix using tree-sitter-sway.

Emacs

Coming soon! Feel free to contribute.

Features

Code Actions

Source: code_actions

Quickly generate boilerplate code and code comments for functions, structs, and ABIs.

Completion

Source: completion.rs

Suggests code to follow partially written statements for functions and variables.

Go to Definition

Jumps to the definition of a symbol from its usage.

Find All References

Locates all occurrences of a symbol throughout the project.

Hover

Source: hover

Provides documentation, compiler diagnostics, and reference links when hovering over functions and variables.

Inlay Hints

Source: inlay_hints.rs

Displays the implied type of a variable next to the variable name. Configurable in Settings.

Rename

Source: rename.rs

Renames a symbol everywhere in the workspace.

Diagnostics

Source: diagnostic.rs

Displays compiler warnings and errors inline.

Syntax Highlighting

Source: highlight.rs

Highlights code based on type and context.

Run

Source: runnable.rs

Shows a button above a runnable function or test.

Troubleshooting

First, confirm you are running the most recent version:

fuelup toolchain install latest
fuelup update
forc-lsp --version

Second, confirm that your $PATH resolves to the forc-lsp binary in $HOME/.fuelup/bin.

which forc-lsp

Slow Performance

If you are experiencing slow performance, you can try the following:

Follow the steps above to ensure you are running the most recent version.

Then, make sure you only have the most recent version of the LSP server running.

pkill forc-lsp

Large projects

Sway projects with ten or more Sway files are likely to have slower LSP performance. We are working on better support for large projects.

In the meantime, if it's too slow, you can disable the LSP server entirely with the sway-lsp.diagnostic.disableLsp setting. The extension will still provide basic syntax highlighting, command palettes, as well as the Sway debugger, but all other language features will be disabled.

Server Logs

You can enable verbose logging of the LSP server.

In VSCode, this is under the setting:

"sway-lsp.trace.server": "verbose"

Once enabled, you can find this in the output window under Sway Language Server.

For other editors, see Installation for links to documentation.

Sway Reference

Sway Libraries

The purpose of Sway Libraries is to contain libraries which users can import and use that are not part of the standard library.

These libraries contain helper functions and other tools valuable to blockchain development.

For more information on how to use a Sway-Libs library, please refer to the Sway-Libs Book.

Assets Libraries

Asset Libraries are any libraries that use Native Assets on the Fuel Network.

Access Control and Security Libraries

Access Control and Security Libraries are any libraries that are built and intended to provide additional safety when developing smart contracts.

Cryptography Libraries

Cryptography Libraries are any libraries that provided cryptographic functionality beyond what the std-lib provides.

  • Bytecode Library; used for on-chain verification and computation of bytecode roots for contracts and predicates.
  • Merkle Proof Library; used to verify Binary Merkle Trees computed off-chain.

Math Libraries

Math Libraries are libraries which provide mathematic functions or number types that are outside of the std-lib's scope.

Data Structures Libraries

Data Structure Libraries are libraries which provide complex data structures which unlock additional functionality for Smart Contracts.

  • Queue Library; a linear data structure that provides First-In-First-Out (FIFO) operations.

Compiler Intrinsics

The Sway compiler supports a list of intrinsics that perform various low level operations that are useful for building libraries. Compiler intrinsics should rarely be used but are preferred over asm blocks because they are type-checked and are safer overall. Below is a list of all available compiler intrinsics:


__size_of_val<T>(val: T) -> u64

Description: Return the size of type T in bytes.

Constraints: None.


__size_of<T>() -> u64

Description: Return the size of type T in bytes.

Constraints: None.


__size_of_str_array<T>() -> u64

Description: Return the size of type T in bytes. This intrinsic differs from __size_of in the case of "string arrays" where the actual length in bytes of the string is returned without padding the byte size to the next word alignment. When T is not a "string array" 0 is returned.

Constraints: None.


__assert_is_str_array<T>()

Description: Throws a compile error if type T is not a "string array".

Constraints: None.


__to_str_array(s: str) -> str[N]

Description: Converts a "string slice" to "string array" at compile time. Parameter "s" must be a string literal.

Constraints: None.


__is_reference_type<T>() -> bool

Description: Returns true if T is a reference type and false otherwise.

Constraints: None.


__is_str_array<T>() -> bool

Description: Returns true if T is a string array and false otherwise.

Constraints: None.


__eq<T>(lhs: T, rhs: T) -> bool

Description: Returns whether lhs and rhs are equal.

Constraints: T is bool, u8, u16, u32, u64, u256, b256 or raw_ptr.


__gt<T>(lhs: T, rhs: T) -> bool

Description: Returns whether lhs is greater than rhs.

Constraints: T is u8, u16, u32, u64, u256, b256.


__lt<T>(lhs: T, rhs: T) -> bool

Description: Returns whether lhs is less than rhs.

Constraints: T is u8, u16, u32, u64, u256, b256.


__gtf<T>(index: u64, tx_field_id: u64) -> T

Description: Returns transaction field with ID tx_field_id at index index, if applicable. This is a wrapper around FuelVM's gtf instruction. The resulting field is cast to T.

Constraints: None.


__addr_of<T>(val: T) -> raw_ptr

Description: Returns the address in memory where val is stored.

Constraints: T is a reference type.


__state_load_word(key: b256) -> u64

Description: Reads and returns a single word from storage at key key.

Constraints: None.


__state_load_quad(key: b256, ptr: raw_ptr, slots: u64) -> bool

Description: Reads slots number of slots (b256 each) from storage starting at key key and stores them in memory starting at address ptr. Returns a Boolean describing whether all the storage slots were previously set.

Constraints: None.


__state_store_word(key: b256, val: u64) -> bool

Description: Stores a single word val into storage at key key. Returns a Boolean describing whether the store slot was previously set.

Constraints: None.


__state_store_quad(key: b256, ptr: raw_ptr, slots: u64) -> bool

Description: Stores slots number of slots (b256 each) starting at address ptr in memory into storage starting at key key. Returns a Boolean describing whether the first storage slot was previously set.

Constraints: None.


__log<T>(val: T)

Description: Logs value val.

Constraints: None.


__add<T>(lhs: T, rhs: T) -> T

Description: Adds lhs and rhs and returns the result.

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256.


__sub<T>(lhs: T, rhs: T) -> T

Description: Subtracts rhs from lhs.

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256.


__mul<T>(lhs: T, rhs: T) -> T

Description: Multiplies lhs by rhs.

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256.


__div<T>(lhs: T, rhs: T) -> T

Description: Divides lhs by rhs.

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256.


__and<T>(lhs: T, rhs: T) -> T

Description: Bitwise AND lhs and rhs.

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256, b256.


__or<T>(lhs: T, rhs: T) -> T

Description: Bitwise OR lhs and rhs.

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256, b256.


__xor<T>(lhs: T, rhs: T) -> T

Description: Bitwise XOR lhs and rhs.

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256, b256.


__mod<T>(lhs: T, rhs: T) -> T

Description: Modulo of lhs by rhs.

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256.


__rsh<T>(lhs: T, rhs: u64) -> T

Description: Logical right shift of lhs by rhs.

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256, b256.


__lsh<T>(lhs: T, rhs: u64) -> T

Description: Logical left shift of lhs by rhs.

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256, b256.


__revert(code: u64)

Description: Reverts with error code code.

Constraints: None.


__ptr_add(ptr: raw_ptr, offset: u64)

Description: Adds offset to the raw value of pointer ptr.

Constraints: None.


__ptr_sub(ptr: raw_ptr, offset: u64)

Description: Subtracts offset to the raw value of pointer ptr.

Constraints: None.


__smo<T>(recipient: b256, data: T, coins: u64)

Description: Sends a message data of arbitrary type T and coins amount of the base asset to address recipient.

Constraints: None.


__not(op: T) -> T

Description: Bitwise NOT of op

Constraints: T is an integer type, i.e. u8, u16, u32, u64, u256, b256.


__jmp_mem()

Description: Jumps to MEM[$hp].

Constraints: None.


__slice(item: &[T; N], start: u64, end: u64) -> &[T]
__slice(item: &[T], start: u64, end: u64) -> &[T]
__slice(item: &mut [T; N], start: u64, end: u64) -> &mut [T]
__slice(item: &mut [T], start: u64, end: u64) -> &mut [T]

Description: Slices an array or another slice.

This intrinsic returns a reference to a slice containing the range of elements inside item. The mutability of reference is defined by the first parameter mutability.

Runtime bound checks are not generated, and must be done manually when and where appropriated. Compile time bound checks are done when possible.

Constraints:

  • item is an array or a slice;
  • when start is a literal, it must be smaller than item length;
  • when end is a literal, it must be smaller than or equal to item length;
  • end must be greater than or equal to start

__elem_at(item: &[T; N], index: u64) -> &T
__elem_at(item: &[T], index: u64) -> &T
__elem_at(item: &mut [T; N], index: u64) -> &mut T
__elem_at(item: &mut [T], index: u64) -> &mut T

Description: Returns a reference to the indexed element. The mutability of reference is defined by the first parameter mutability.

Runtime bound checks are not generated, and must be done manually when and where appropriated. Compile time bound checks are done when possible.

Constraints:

  • item is a reference to an array or a reference to a slice;
  • when index is a literal, it must be smaller than item length;

Attributes

The Sway compiler supports a list of attributes that perform various operations that are useful for building, testing and documenting Sway programs. Below is a list of all available attributes:

Allow

The #[allow(...)] attribute overrides checks so that violations will go unreported. The following checks can be disabled:

  • #[allow(dead_code)] disable checks for dead code;
  • #[allow(deprecated)] disables checks for usage of deprecated structs, functions and other items.

Doc

The #[doc(..)] attribute specifies documentation.

Line doc comments beginning with exactly three slashes ///, are interpreted as a special syntax for doc attributes. That is, they are equivalent to writing #[doc("...")] around the body of the comment, i.e., /// Foo turns into #[doc("Foo")]

Line comments beginning with //! are doc comments that apply to the module of the source file they are in. That is, they are equivalent to writing #![doc("...")] around the body of the comment. //! module level doc comments should be at the top of Sway files.

Documentation can be generated from doc attributes using forc doc.

Inline

The inline attribute suggests that a copy of the attributed function should be placed in the caller, rather than generating code to call the function where it is defined.

Note: The Sway compiler automatically inlines functions based on internal heuristics. Incorrectly inlining functions can make the program slower, so this attribute should be used with care.

The #[inline(never)] attribute suggests that an inline expansion should never be performed.

The #[inline(always)] attribute suggests that an inline expansion should always be performed.

Note: #[inline(..)] in every form is a hint, with no requirements on the language to place a copy of the attributed function in the caller.

Payable

The lack of #[payable] implies the method is non-payable. When calling an ABI method that is non-payable, the compiler emits an error if the amount of coins forwarded with the call is not guaranteed to be zero. Note that this is strictly a compile-time check and does not incur any runtime cost.

Storage

In Sway, functions are pure by default but can be opted into impurity via the storage function attribute. The storage attribute may take read and/or write arguments indicating which type of access the function requires.

The #[storage(read)] attribute indicates that a function requires read access to the storage.

The #[storage(write)] attribute indicates that a function requires write access to the storage.

More details in Purity.

Test

The #[test] attribute marks a function to be executed as a test.

The #[test(should_revert)] attribute marks a function to be executed as a test that should revert.

More details in Unit Testing.

Deprecated

The #[deprecated] attribute marks an item as deprecated and makes the compiler emit a warning for every usage of the deprecated item. This warning can be disabled using #[allow(deprecated)].

It is possible to improve the warning message with #[deprecated(note = "your message")]

Fallback

The #[fallback] attribute makes the compiler use the marked function as the contract call fallback function, which means that, when a contract is called, and the contract selection fails, the fallback function will be called instead.

Style Guide

Capitalization

In Sway, structs, traits, and enums are CapitalCase. Modules, variables, and functions are snake_case, constants are SCREAMING_SNAKE_CASE. The compiler will warn you if your capitalization is ever unidiomatic.

Known Issues and Workarounds

Known Issues

  • #870: All impl blocks need to be defined before any of the functions they define can be called. This includes sibling functions in the same impl declaration, i.e., functions in an impl can't call each other yet.

Missing Features

  • #1182 Arrays in a storage block are not yet supported. See the Manual Storage Management section for details on how to use store and get from the standard library to manage storage slots directly. Note, however, that StorageMap<K, V> does support arbitrary types for K and V without any limitations.

General

  • No compiler optimization passes have been implemented yet, therefore bytecode will be more expensive and larger than it would be in production. Note that eventually the optimizer will support zero-cost abstractions, avoiding the need for developers to go down to inline assembly to produce optimal code.

Behavior Considered Undefined

Sway code that contains any of the following behavior is considered undefined. The compiler is allowed to treat undefined Sway code however it desires, including removing it or replacing it with any other Sway code.

This is not an exhaustive list, it may grow or shrink, there is no formal model of Sway's semantics so there may be more behavior considered undefined. We reserve the right to make some of the listed behavior defined in the future.

  • Invalid arithmetic operations (overflows, underflows, division by zero, etc.).
  • Misuse of compiler intrinsics.
  • Incorrect use of inline assembly.
  • Reading and writing raw_ptr and raw_slice.
  • Slicing and indexing out of bounds by directly using compiler intrinsics.
  • Modifying collections while iterating over them using Iterators.

Differences From Solidity

This page outlines some of the critical differences between Sway and Solidity, and between the FuelVM and the EVM.

Underlying Virtual Machine

The underlying virtual machine targeted by Sway is the FuelVM, specified here. Solidity targets the Ethereum Virtual Machine (EVM), specified here.

Word Size

Words in the FuelVM are 64 bits (8 bytes), rather than the 256 bits (32 bytes) of the EVM. Therefore, all primitive integers smaller and including u64 are stored in registers; u256, being bigger than the registers, and hashes (the b256 type) are not stored in registers but rather in memory. They are therefore pointers to a 32-byte memory region containing their data.

Unsigned Integers Only

Only unsigned integers are provided as primitives: u8, u16, u32, u64, and u256. Signed integer arithmetic is not available in the FuelVM. Signed integers and signed integer arithmetic can be implemented in high-level libraries if needed.

Global Revert

Panics in the FuelVM (called "reverts" in Solidity and the EVM) are global, i.e. they cannot be caught. A panic will completely and unconditionally revert the stateful effects of a transaction, minus gas used.

Default Safe Math

Math in the FuelVM is by default safe (i.e. any overflow or exception is a panic). Safety checks are performed natively in the VM implementation, rather than at the bytecode level like Solidity's default safe math.

No* Code Size Limit

There is no practical code size limit to Sway contracts. The physical limit is governed by the VM_MAX_RAM VM parameter, which at the time of writing is 64 MiB.

Account Types

Account types in the FuelVM have type-safe wrappers around primitive b256 hashes to clearly distinguish their respective types. The wrapper Address mirrors that of an EOA (Externally Owned Account) and has the ability to hold UTXOs in the context of the EVM. The other wrapper, ContractId, reflects that of a deployed contract in the EVM but cannot hold UTXOs.

Differences From Rust

Sway shares a lot with Rust, especially its syntax. Because they are so similar, you may be surprised or caught off guard when they differ. This page serves to outline, from a high level, some of the syntactic gotchas that you may encounter.

Enum Variant Syntax

In Rust, enums generally take one of three forms: unit variants, which have no inner data, struct variants, which contain named fields, and tuple variants, which contain within them a tuple of data. If you are unfamiliar with these terms, this is what they look like:

// note to those skimming the docs: this is Rust syntax! Not Sway! Don't copy/paste this into a Sway program.

enum Foo {
    UnitVariant,
    TupleVariant(u32, u64, bool),
    StructVariant {
        field_one: bool,
        field_two: bool
    }
}

In Sway, enums are simplified. Enums variants must all specify exactly one type. This type represents their interior data. This is actually isomorphic to what Rust offers, but with a different syntax. You can see the above enum but with Sway syntax below:

// This is equivalent Sway syntax for the above Rust enum.
enum Foo {
    UnitVariant: (),
    TupleVariant: (u32, u64, bool),
    StructVariant: MyStruct,
}

struct MyStruct {
    field_one: bool,
    field_two: bool,
}

Memory Allocation

In Rust, the borrow checker implements Rust's ownership system

In Sway, there is no borrow checker. This means there is no concept of ownership, borrowing, or lifetimes. Instead, objects are copied and moved similar to C++. Also Sway does not have any destructors nor Drop traits. This means allocated memory lives for the entire transaction and is not deallocated until the end of the transaction. A transaction may allocate up to 64 MB of memory.

Contributing To Sway

Thanks for your interest in contributing to Sway! This document outlines the process for installing and setting up the Sway toolchain for development, as well as some conventions on contributing to Sway.

If you run into any difficulties getting started, you can always ask questions on our Discourse.

Building and setting up a development workspace

See the introduction section for instructions on installing and setting up the Sway toolchain.

Getting the repository

  1. Visit the Sway repo and fork the project.
  2. Then clone your forked copy to your local machine and get to work.
git clone https://github.com/FuelLabs/sway
cd sway

Building and testing

The following steps will run the sway test suite and ensure that everything is set up correctly.

First, open a new terminal and start fuel-core with:

fuel-core

Then open a second terminal, cd into the sway repo and run:

cargo run --bin test

After the test suite runs, you should see:

Tests passed.
_n_ tests run (0 skipped)

Congratulations! You've now got everything setup and are ready to start making contributions.

Finding something to work on

There are many ways in which you may contribute to the Sway project, some of which involve coding knowledge and some which do not. A few examples include:

  • Reporting bugs
  • Adding documentation to the Sway book
  • Adding new features or bug fixes for which there is already an open issue
  • Making feature requests

Check out our Help Wanted, Sway Book or Good First Issue issues to find a suitable task.

If you are planning something big, for example, related to multiple components or changes current behaviors, make sure to open an issue to discuss with us before starting on the implementation.

Contribution flow

This is a rough outline of what a contributor's workflow looks like:

  • Make sure what you want to contribute is already tracked as an issue.
    • We may discuss the problem and solution in the issue.
  • Create a Git branch from where you want to base your work. This is usually master.
  • Write code, add test cases, and commit your work.
  • Run tests and make sure all tests pass.
  • If the PR contains any breaking changes, add the breaking label to your PR.
  • Push your changes to a branch in your fork of the repository and submit a pull request.
    • Make sure to mention the issue, which is created at step 1, in the commit message.
  • Your PR will be reviewed and some changes may be requested.
    • Once you've made changes, your PR must be re-reviewed and approved.
    • If the PR becomes out of date, you can use GitHub's 'update branch' button.
    • If there are conflicts, you can merge and resolve them locally. Then push to your PR branch. Any changes to the branch will require a re-review.
  • Our CI system (Github Actions) automatically tests all authorized pull requests.
  • Use Github to merge the PR once approved.

Thanks for your contributions!

Linking issues

Pull requests should be linked to at least one issue in the same repo.

If the pull request resolves the relevant issues, and you want GitHub to close these issues automatically after it merged into the default branch, you can use the syntax (KEYWORD #ISSUE-NUMBER) like this:

close #123

If the pull request links an issue but does not close it, you can use the keyword ref like this:

ref #456

Multiple issues should use full syntax for each issue and separate by a comma, like:

close #123, ref #456

Keywords

The following list contains keywords that are reserved for current or future use by the Sway language. As such, they cannot be used as identifiers. Identifiers are names of functions, variables, parameters, modules, constants, attributes, types or traits, etc.

Keywords Currently in Use

The following is a list of keywords currently in use, with their functionality described.

  • as - rename items in use statements, e.g., use type::a as alias_name
  • abi - defines a smart contract ABI in a syntactically similar way to traits
  • break - exit a loop immediately
  • const - define constant items
  • continue - continue to the next loop iteration
  • else - used in conjunction with if conditions for control flow constructs
  • enum - define an enumeration
  • false - Boolean false literal
  • fn- define a function or the function pointer type
  • if - branch based on the result of a conditional expression
  • impl - implement inherent or trait functionality
  • let - bind a variable
  • match - exhaustively match a value to patterns
  • mod - define a module
  • mut - denote mutability in references, or pattern bindings
  • pub - denote public visibility of Sway data structures, traits, or modules
  • ref - bind by reference
  • return - return early from a function
  • Self - a type alias for the type we are defining or implementing
  • self - method subject
  • struct - define a structure
  • trait - define a trait
  • true - Boolean true literal
  • type - define a type alias or associated type
  • use - bring symbols into scope
  • where - specifies traits for generic types
  • while - loop conditionally based on the result of an expression

Keywords Reserved for Possible Future Use

  • abstract
  • async
  • await
  • become
  • box
  • do
  • dyn
  • extern
  • for
  • in
  • loop
  • macro
  • move
  • override
  • priv
  • static
  • super
  • try
  • typeof
  • unsafe
  • unsized
  • virtual
  • yield

Special Keywords

Program Keywords

Keywords associated with defining the type of Sway program to compile

  • contract - analogous to a deployed API with some database state
  • library - Sway code that defines new common behavior
  • predicate - programs that return a Boolean value and which represent ownership of some resource upon execution to true
  • script - a runnable bytecode on the chain, which executes once to perform a task

Attribute Keywords

Keywords associated with defining the functionality of attributes

  • allow - overrides checks that would otherwise result in errors or warnings
  • doc - specifies documentation
  • inline - suggests that a copy of the attributed function should be placed in the caller, rather than generating code to call the function where it is defined
  • payable - implies method is payable for compile time
  • storage - declaration that contains a list of stored variables
  • test - marks a function to be executed as a test
  • deprecated - marks an item as deprecated

Forc Reference

Forc stands for Fuel Orchestrator. Forc provides a variety of tools and commands for developers working with the Fuel ecosystem, such as scaffolding a new project, formatting, running scripts, deploying contracts, testing contracts, and more. If you're coming from a Rust background, forc is similar to cargo.

If you are new to Forc, see the Forc Project introduction section.

For a comprehensive overview of the Forc CLI commands, see the Commands section.

Manifest Reference

The Forc.toml (the manifest file) is a compulsory file for each package and it is written in [TOML] format. Forc.toml consists of the following fields:

  • [project] — Defines a sway project.

    • name — The name of the project.
    • version — The version of the project.
    • description — A description of the project.
    • authors — The authors of the project.
    • organization — The organization of the project.
    • license — The project license.
    • homepage — URL of the project homepage.
    • repository — URL of the project source repository.
    • documentation — URL of the project documentation.
    • categories — Categories of the project.
    • keywords — Keywords the project.
    • entry — The entry point for the compiler to start parsing from.
      • For the recommended way of selecting an entry point of large libraries please take a look at: Libraries
    • implicit-std - Controls whether provided std version (with the current forc version) will get added as a dependency implicitly. Unless you know what you are doing, leave this as default.
    • forc-version - The minimum forc version required for this project to work properly.
    • metadata - Metadata for the project; can be used by tools which would like to store package configuration in Forc.toml.
  • [dependencies] — Defines the dependencies.

  • [network] — Defines a network for forc to interact with.

    • url — URL of the network.
  • [build-profile] - Defines the build profiles.

  • [patch] - Defines the patches.

  • [contract-dependencies] - Defines the contract dependencies.

The [project] section

An example Forc.toml is shown below. Under [project] the following fields are optional:

  • authors
  • organization
  • version
  • description
  • homepage
  • repository
  • documentation
  • categories
  • keywords

Also for the following fields, a default value is provided so omitting them is allowed:

  • entry - (default : main.sw )
  • implicit-std - (default : true )
[project]
authors = ["user"]
entry = "main.sw"
description = "Wallet contract"
version = "1.0.0"
homepage = "https://example.com/"
repository = "https://example.com/"
documentation = "https://example.com/"
organization = "Fuel_Labs"
license = "Apache-2.0"
name = "wallet_contract"
categories = ["example"]
keywords = ["example"]

[project.metadata]
indexing = { namespace = "counter-contract", schema_path = "out/release/counter-contract-abi.json" }

Metadata Section in Forc.toml

The [project.metadata] section provides a dedicated space for external tools and plugins to store their configuration in Forc.toml. The metadata key names are arbitrary and do not need to match the tool's name.

Workspace vs Project Metadata

Metadata can be defined at two levels:

Workspace level - defined in the workspace's root Forc.toml:

[workspace.metadata]
my_tool = { shared_setting = "value" }

Project level - defined in individual project's Forc.toml:

[project.metadata.any_name_here]
option1 = "value"
option2 = "value"

[project.metadata.my_custom_config]
setting1 = "value"
setting2 = "value"

Example for an indexing tool:

[project.metadata.indexing]
namespace = "counter-contract"
schema_path = "out/release/counter-contract-abi.json"

When both workspace and project metadata exist:

  • Project-level metadata should take precedence over workspace metadata
  • Tools can choose to merge workspace and project settings
  • Consider documenting your tool's metadata inheritance behavior

Guidelines for Plugin Developers

Best Practices

  • Choose clear, descriptive metadata key names
  • Document the exact metadata key name your tool expects
  • Don't require Forc.toml if tool can function without it
  • Consider using TOML format for dedicated config files
  • Specify how your tool handles workspace vs project metadata

Implementation Notes

  • The metadata section is optional
  • Forc does not parse metadata contents
  • Plugin developers handle their own configuration parsing
  • Choose unique metadata keys to avoid conflicts with other tools

Example Use Cases

  • Documentation generation settings
  • Formatter configurations
  • Debugger options
  • Wallet integration
  • Contract indexing
  • Testing frameworks

This allows for a streamlined developer experience while maintaining clear separation between core Forc functionality and third-party tools.

External Tooling Examples

  • forc-index-ts: A TypeScript CLI tool for parsing Forc.toml metadata to read contract ABI JSON file.
  • forc-index-rs: A Rust CLI tool for parsing Forc.toml metadata to read contract ABI JSON file.

The [dependencies] section

The following fields can be provided with a dependency:

  • version - Desired version of the dependency
  • path - The path of the dependency (if it is local)
  • git - The URL of the git repo hosting the dependency
  • branch - The desired branch to fetch from the git repo
  • tag - The desired tag to fetch from the git repo
  • rev - The desired rev (i.e. commit hash) reference

Please see dependencies for details

The [network] section

For the following fields, a default value is provided so omitting them is allowed:

The [build-profile.*] section

The [build-profile] tables provide a way to customize compiler settings such as debug options.

The following fields can be provided for a build-profile:

  • print-ast - Whether to print out the generated AST or not, defaults to false.
  • print-dca-graph - Whether to print out the computed Dead Code Analysis (DCA) graph (in GraphViz DOT format), defaults to false.
  • print-dca-graph-url-format - The URL format to be used in the generated DOT file, an example for VS Code would be: vscode://file/{path}:{line}:{col}.
  • print-ir - Whether to print out the generated Sway IR (Intermediate Representation) or not, defaults to false.
  • print-asm - Whether to print out the generated ASM (assembler), defaults to false.
  • terse - Terse mode. Limited warning and error output, defaults to false.
  • time_phases - Whether to output the time elapsed over each part of the compilation process, defaults to false.
  • include_tests - Whether or not to include test functions in parsing, type-checking, and code generation. This is set to true by invocations like forc test, but defaults to false.
  • error_on_warnings - Whether to treat errors as warnings, defaults to false.

There are two default [build-profile] available with every manifest file. These are debug and release profiles. If you want to override these profiles, you can provide them explicitly in the manifest file like the following example:

[project]
authors = ["user"]
entry = "main.sw"
organization = "Fuel_Labs"
license = "Apache-2.0"
name = "wallet_contract"

[build-profile.debug]
print-asm = { virtual = false, allocated = false, final = true }
print-ir = { initial = false, final = true, modified = false, passes = []}
terse = false

[build-profile.release]
print-asm = { virtual = true, allocated = false, final = true }
print-ir = { initial = true, final = false, modified = true, passes = ["dce", "sroa"]}
terse = true

Since release and debug are implicitly included in every manifest file, you can use them by just passing --release or by not passing anything (debug is default). For using a user defined build profile there is --build-profile <profile name> option available to the relevant commands. (For an example see forc-build)

Note that providing the corresponding CLI options (like --asm) will override the selected build profile. For example if you pass both --release and --asm all, release build profile is overridden and resulting build profile would have a structure like the following:

print-ast = false
print-ir = { initial = false, final = false, modified = false, passes = []}
print-asm = { virtual = true, allocated = true, final = true }
terse = false
time-phases = false
include-tests = false
error-on-warnings = false
experimental-private-modules = false

The [patch] section

The [patch] section of Forc.toml can be used to override dependencies with other copies. The example provided below patches https://github.com/fuellabs/sway with the test branch of the same repo.

[project]
authors = ["user"]
entry = "main.sw"
organization = "Fuel_Labs"
license = "Apache-2.0"
name = "wallet_contract"

[dependencies]

[patch.'https://github.com/fuellabs/sway']
std = { git = "https://github.com/fuellabs/sway", branch = "test" }

In the example above, std is patched with the test branch from std repo. You can also patch git dependencies with dependencies defined with a path.

[patch.'https://github.com/fuellabs/sway']
std = { path = "/path/to/local_std_version" }

Just like std you can also patch dependencies you declared with a git repo.

[project]
authors = ["user"]
entry = "main.sw"
organization = "Fuel_Labs"
license = "Apache-2.0"
name = "wallet_contract"

[dependencies]
foo = { git = "https://github.com/foo/foo", branch = "master" }

[patch.'https://github.com/foo']
foo = { git = "https://github.com/foo/foo", branch = "test" }

Note that each key after the [patch] is a URL of the source that is being patched.

The [contract-dependencies] section

The [contract-dependencies] table can be used to declare contract dependencies for a Sway contract or script. Contract dependencies are the set of contracts that our contract or script may interact with. Declaring [contract-dependencies] makes it easier to refer to contracts in your Sway source code without having to manually update IDs each time a new version is deployed. Instead, we can use forc to pin and update contract dependencies just like we do for regular library dependencies.

Contracts declared under [contract-dependencies] are built and pinned just like regular [dependencies] however rather than importing each contract dependency's entire public namespace we instead import their respective contract IDs as CONTRACT_ID constants available via each contract dependency's namespace root. This means you can use a contract dependency's ID as if it were declared as a pub const in the root of the contract dependency package as demonstrated in the example below.

Entries under [contract-dependencies] can be declared in the same way that [dependencies] can be declared. That is, they can refer to the path or git source of another contract. Note that entries under [contract-dependencies] must refer to contracts and will otherwise produce an error.

Example Forc.toml:

[project]
authors = ["user"]
entry = "main.sw"
organization = "Fuel_Labs"
license = "Apache-2.0"
name = "wallet_contract"

[contract-dependencies]
foo = { path = "../foo" }

Example usage:

script;

fn main() {
  let foo_id = foo::CONTRACT_ID;
}

Because the ID of a contract is computed deterministically, rebuilding the same contract would always result in the same contract ID. Since two contracts with the same contract ID cannot be deployed on the blockchain, a "salt" factor is needed to modify the contract ID. For each contract dependency declared under [contract-dependencies], salt can be specified. An example is shown below:

[contract-dependencies]
foo = { path = "../foo", salt = "0x1000000000000000000000000000000000000000000000000000000000000000" }

For contract dependencies that do not specify any value for salt, a default of all zeros for salt is implicitly applied.

Workspaces

A workspace is a collection of one or more packages, namely workspace members, that are managed together.

The key points for workspaces are:

  • Common forc commands available for a single package can also be used for a workspace, like forc build or forc deploy.
  • All packages share a common Forc.lock file which resides in the root directory of the workspace.

Workspace manifests are declared within Forc.toml files and support the following fields:

  • members - Packages to include in the workspace.
  • [patch] - Defines the patches.

An empty workspace can be created with forc new --workspace or forc init --workspace.

The members field

The members field defines which packages are members of the workspace:

[workspace]
members = ["member1", "path/to/member2"]

The members field accepts entries to be given in relative path with respect to the workspace root. Packages that are located within a workspace directory but are not contained within the members set are ignored.

The [patch] section

The [patch] section can be used to override any dependency in the workspace dependency graph. The usage is the same with package level [patch] section and details can be seen here.

It is not allowed to declare patch table in member of a workspace if the workspace manifest file contains a patch table.

Example:

[workspace]
members = ["member1", "path/to/member2"]


[patch.'https://github.com/fuellabs/sway']
std = { git = "https://github.com/fuellabs/sway", branch = "test" }

In the above example each occurrence of std as a dependency in the workspace will be changed with std from test branch of sway repo.

Some forc commands that support workspaces

  • forc build - Builds an entire workspace.
  • forc deploy - Builds and deploys all deployable members (i.e, contracts) of the workspace in the correct order.
  • forc run - Builds and runs all scripts of the workspace.
  • forc check - Checks all members of the workspace.
  • forc update - Checks and updates workspace level Forc.lock file that is shared between workspace members.
  • forc clean - Cleans all output artifacts for each member of the workspace.
  • forc fmt - Formats all members of a workspace.

Dependencies

Forc has a dependency management system which can pull packages using git and ipfs. This allows users to build and share Forc libraries.

Adding a dependency

If your Forc.toml doesn't already have a [dependencies] table, add one. Below, list the package name alongside its source. Currently, forc supports git, ipfs and path sources.

If a git source is specified, forc will fetch the git repository at the given URL and then search for a Forc.toml for a package with the given name anywhere inside the git repository.

The following example adds a library dependency named custom_lib. For git dependencies you may optionally specify a branch, tag, or rev (i.e. commit hash) reference.

[dependencies]
custom_lib = { git = "https://github.com/FuelLabs/custom_lib", branch = "master" }
# custom_lib = { git = "https://github.com/FuelLabs/custom_lib", tag = "v0.0.1" }
# custom_lib = { git = "https://github.com/FuelLabs/custom_lib", rev = "87f80bdf323e2d64e213895d0a639ad468f4deff" }

Depending on a local library using path:

[dependencies]
custom_lib = { path = "../custom_lib" }

For ipfs sources, forc will fetch the specified cid using either a local ipfs node or a public gateway. forc automatically tries to connect to local ipfs node. If it fails, it defaults to using https://ipfs.io/ as a gateway.

The following example adds a dependency with an ipfs source.

[dependencies]
custom_lib = { ipfs = "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG" }

Once the package is added, running forc build will automatically download added dependencies.

Updating dependencies

To update dependencies in your Forc directory you can run forc update. For path and ipfs dependencies this will have no effect. For git dependencies with a branch reference, this will update the project to use the latest commit for the given branch.

Here are a list of commands available to forc:

forc addr2line

forc build

forc check

forc clean

forc completions

forc contract-id

forc init

forc new

forc parse-bytecode

forc plugins

forc predicate-root

forc test

forc update

forc template

Plugins

Plugins can be used to extend forc with new commands that go beyond the native commands mentioned in the previous chapter. While the Fuel ecosystem provides a few commonly useful plugins (forc-fmt, forc-client, forc-lsp, forc-explore), anyone can write their own!

Let's install a plugin, forc-explore, and see what's underneath the plugin:

cargo install forc-explore

Check that we have installed forc-explore:

$ forc plugins
Installed Plugins:
forc-explore

forc-explore runs the Fuel Network Explorer, which you can run and check out for yourself:

$ forc explore
Fuel Network Explorer 0.1.1
Running server on http://127.0.0.1:3030
Server::run{addr=127.0.0.1:3030}: listening on http://127.0.0.1:3030

You can visit http://127.0.0.1:3030 to check out the network explorer!

Note that some plugin crates can also provide more than one command. For example, installing the forc-client plugin provides the forc deploy and forc run commands. This is achieved by specifying multiple [[bin]] targets within the forc-client manifest.

Writing your own plugin

We encourage anyone to write and publish their own forc plugin to enhance their development experience.

Your plugin must be named in the format forc-<MY_PLUGIN> and you may use the above template as a starting point. You can use clap and add more subcommands, options and configurations to suit your plugin's needs.

forc-client

The forc plugin for interacting with a Fuel node.

Since transactions are going to require some gas, you need to sign them with an account that has enough coins to pay for them.

We offer multiple ways to sign the transaction:

  1. Sign the transaction via your local wallet using forc-client which integrates with our CLI wallet, forc-wallet.
  2. Use the default signer to deploy to a local node
  3. Use forc-wallet to manually sign transactions, and copy the signed transaction back to forc-client.

The easiest and recommended way to interact with deployed networks such as our testnets is option 1, using forc-client to sign your transactions which reads your default forc-wallet vault. For interacting with local node, we recommend using the second option, which leads forc-client to sign transactions with the private key that comes pre-funded in local environments.

Option 1: Sign transactions via forc-client using your local forc-wallet vault

If you've used forc-wallet before, you'll already have a secure, password-protected vault holding your private key written to your file-system. forc-client is compatible with forc-wallet such that it can read that vault by asking you your password and use your account to sign transactions.

Example:

> forc deploy

    Building /Users/yourname/test-projects/test-contract
    Finished release [optimized + fuel] target(s) in 11.39s
  Confirming transactions [deploy impl-contract]
             Network: https://testnet.fuel.network
             Wallet: /Users/yourname/.fuel/wallets/.wallet
✔ Wallet password · ********
? Wallet account ›
❯ [0] fuel12pls73y9hnqdqthvduy2x44x48zt8s50pkerf32kq26f2afeqdwq6rj9ar - 0.002197245 ETH
  [1] fuel1vzrm6kw9s3tv85gl25lpptsxrdguyzfhq6c8rk07tr6ft5g45nwqqh0uty - 0.001963631 ETH
? Do you agree to sign 1 transaction? (y/n) › yes
     Finished deploying impl-contract https://app.fuel.network/contract/0x94b712901f04332682d14c998a5fc5a078ed15321438f46d58d0383200cde43d
     Deployed in block https://app.fuel.network/block/5958351

As it can be seen from the example, forc-client asks for your password to decrypt the forc-wallet vault, and list your accounts so that you can select the one you want to fund the transaction with.

Option 2: Using default signer

If you are not interacting with a deployed network, such as testnets, your local fuel-core environment can be structured such that it funds an account by default. Using --default-signer flag with forc-client binaries (run, deploy) will instruct forc-client to sign transactions with this pre-funded account. This makes it a useful command while working against a local node.

Example:

> forc deploy --default-signer

    Building /Users/test/test-projects/test-contract
    Finished release [optimized + fuel] target(s) in 11.40s
  Confirming transactions [deploy impl-contract]
             Network: http://127.0.0.1:4000
    Finished deploying impl-contract 0xf9fb08ef18ce226954270d6d4f67677d484b8782a5892b3d436572b405407544
    Deployed in block 00000001

Option 3: Manually signing through forc-wallet (Deprecated)

This option is for creating the transaction first, signing it manually, and supplying the signed transaction back to forc-client. Since it requires multiple steps, it is more error-prone and not recommended for general use cases. Also this will be deprecated soon.

  1. Construct the transaction by using either forc deploy or forc run. To do so simply run forc deploy --manual-sign or forc run --manual-sign with your desired parameters. For a list of parameters please refer to the forc-deploy or forc-run section of the book. Once you run either command you will be asked the address of the wallet you are going to be signing with. After the address is given the transaction will be generated and you will be given a transaction ID. At this point CLI will actively wait for you to insert the signature.
  2. Take the transaction ID generated in the first step and sign it with forc wallet sign --account <account_index> tx-id <transaction_id>. This will generate a signature.
  3. Take the signature generated in the second step and provide it to forc-deploy (or forc-run). Once the signature is provided, the signed transaction will be submitted.

Other useful commands of forc-wallet

  • You can see a list of existing accounts with accounts command.
forc wallet accounts
  • If you want to retrieve the address for an account by its index you can use account command.
forc wallet account <account_index>

If you want to sign the transaction generated by forc-deploy or forc-run with an account funded by default once you start your local node, you can pass --default-signer to them. Please note that this will only work against your local node.

forc-deploy --default-signer
forc-run --default-signer

By default --default-signer flag would sign your transactions with the following private-key:

0xde97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c

Selecting a target network

By default, local is used for the target network. To interact with the latest testnet, use the --testnet flag. When this flag is passed, transactions created by forc-deploy will be sent to the latest testnet:

forc-deploy --testnet

The same can be done to target mainnet:

forc-deploy --mainnet

It is also possible to pass the exact node URL while using forc-deploy or forc-run which can be done using --node-url flag:

forc-deploy --node-url https://mainnet.fuel.network

Another alternative is the --target option, which provides useful aliases to all targets. For example if you want to deploy to testnet you can use:

forc-deploy --target testnet

Since deploying and running projects on the testnet cost gas, you will need coins to pay for them. You can get some using the testnet faucet.

Delayed transactions

For delayed transactions, you can use the --submit-only flag. This flag allows you to submit the transaction without waiting for its finalization.

One use case for this is multisig transactions, where a deployment transaction may stay in a pending state while waiting for all signatures.

forc-deploy --submit-only

Deployment Artifacts

forc-deploy saves the details of each deployment in the out/deployments folder within the project's root directory. Below is an example of a deployment artifact:

{
  "transaction_id": "0xec27bb7a4c8a3b8af98070666cf4e6ea22ca4b9950a0862334a1830520012f5d",
  "salt": "0x9e35d1d5ef5724f29e649a3465033f5397d3ebb973c40a1d76bb35c253f0dec7",
  "network_endpoint": "http://127.0.0.1:4000",
  "chain_id": 0,
  "contract_id": "0x767eeaa7af2621e637f9785552620e175d4422b17d4cf0d76335c38808608a7b",
  "deployment_size": 68,
  "deployed_block_id": "0x915c6f372252be6bc54bd70df6362dae9bf750ba652bf5582d9b31c7023ca6cf"
}

Proxy Contracts

forc-deploy supports deploying proxy contracts automatically if it is enabled in the Forc.toml of the contract.

[project]
name = "test_contract"
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
implicit-std = false

[proxy]
enabled = true

If there is no address field present under the proxy table, like the example above, forc will automatically create a proxy contract based on the SRC-14 implementation from sway-standards. After generating and deploying the proxy contract, the target is set to the current contract, and the owner of the proxy is set to the account that is signing the transaction for deployment.

This means that if you simply enable proxy in the Forc.toml, forc will automatically deploy a proxy contract for you and you do not need to do anything manually aside from signing the deployment transactions for the proxy contract. After deploying the proxy contract, the address is added into the address field of the proxy table.

If you want to update the target of an SRC-14 compliant proxy contract rather than deploying a new one, simply add its address in the address field, like the following example:

[project]
name = "test_contract"
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
implicit-std = false

[proxy]
enabled = true
address = "0xd8c4b07a0d1be57b228f4c18ba7bca0c8655eb6e9d695f14080f2cf4fc7cd946" # example proxy contract address

If an address is present, forc calls into that contract to update its target instead of deploying a new contract. Since a new proxy deployment adds its own address into the Forc.toml automatically, you can simply enable the proxy once and after the initial deployment, forc will keep updating the target accordingly for each new deployment of the same contract.

Large Contracts

For contracts over the maximum contract size limit (currently 100kB) defined by the network, forc-deploy will split the contract into chunks and deploy the contract with multiple transactions using the Rust SDK's loader contract functionality. Chunks that have already been deployed will be reused on subsequent deployments.

Deploying Scripts and Predicates

forc deploy now supports deploying scripts and predicates in addition to contracts. These are deployed as blobs with generated loaders for efficiency.

Scripts and predicates are deployed automatically when you run forc deploy on a project that contains them. The deployment process differs slightly from contract deployment:

  1. For scripts and predicates, the bytecode is uploaded as a blob.
  2. A loader is generated that can load and execute the blob.
  3. The loader bytecode is saved in the project's output directory.

After deployment, you'll find new files in your project's output directory:

  • For scripts: <script_name>-loader.bin
  • For predicates: <predicate_name>-loader.bin and <predicate_name>-loader-root

The loader files contain the bytecode necessary to load and execute your script or predicate from the deployed blob.

This new deployment method allows for more efficient storage and execution of scripts and predicates on the Fuel network.

Note: Contracts are still deployed directly, not as blobs given that the contract size is under the maximum contract size limit defined by network (currently 100kB).

forc deploy

forc run

forc submit

Forc Call

forc-call is a command-line tool for interacting with deployed Fuel contracts. It allows you to make contract calls, query contract state, and interact with any deployed contract on the Fuel network - all from your command line!

The forc call command is part of the Forc toolchain and is installed alongside other Forc tools.

Getting Started

Here are a few examples of what you can do with forc call:

Call a simple addition function on a deployed contract (in dry-run mode):

contract;

abi ContractABI {
  fn add(a: u64, b: u64) -> u64;
}

impl ContractABI for Contract {
  fn add(a: u64, b: u64) -> u64 {
    a + b
  }
}
forc call 0xe18de7c7c8c61a1c706dccb3533caa00ba5c11b5230da4428582abf1b6831b4d \
  --abi ./out/debug/counter-contract-abi.json \
  add 1 2

Query the owner of a deployed DEX contract on testnet:

forc call \
  --testnet \
  --abi https://raw.githubusercontent.com/mira-amm/mira-v1-periphery/refs/heads/main/fixtures/mira-amm/mira_amm_contract-abi.json \
  0xd5a716d967a9137222219657d7877bd8c79c64e1edb5de9f2901c98ebe74da80 \
  owner

Usage

The basic syntax for forc call is:

forc call [OPTIONS] --abi <ABI-PATH/URL> <CONTRACT_ID> <SELECTOR> [ARGS]...

Where the following arguments are required:

  • CONTRACT_ID is the ID of the deployed contract you want to interact with
  • ABI-PATH/URL is the path or URL to the contract's JSON ABI file
  • SELECTOR is the function name (selector) you want to call
  • ARGS are the arguments to pass to the function

Type Encoding

When passing arguments to contract functions, values are encoded according to their Sway types. Here's how to format different types:

TypesExample inputNotes
booltrue or false
u8, u16, u32, u64, u128, u25642
b2560x0000000000000000000000000000000000000000000000000000000000000042 or 00000000000000000000000000000000000000000000000000000000000000420x prefix is optional
bytes, RawSlice0x42 or 420x prefix is optional
String, StringSlice, StringArray (Fixed-size)"abc"
Tuple(42, true)The types in tuple can be different
Array (Fixed-size), Vector (Dynamic)[42, 128]The types in array or vector must be the same; i.e. you cannot have [42, true]
Struct{42, 128}Since structs are packed encoded, the attribute names are not encoded; i.e. {42, 128}; this could represent the following struct Polygon { x: u64, y: u64 }
Enum(Active: true) or (1: true)Enums are key-val pairs with keys as being variant name (case-sensitive) or variant index (starting from 0) and values as being the variant value; this could represent the following enum MyEnum { Inactive, Active(bool) }

ABI Support

The ABI (Application Binary Interface) can be provided in two ways.

Local file

forc call <CONTRACT_ID> --abi ./path/to/abi.json <FUNCTION> [ARGS...]

Remote ABI file/URL

forc call <CONTRACT_ID> --abi https://example.com/abi.json <FUNCTION> [ARGS...]

Network Configuration

forc call --node-url http://127.0.0.1:4000 ...
# or
forc call --target local ...

Advanced Usage

Using Wallets

# utilising the forc-wallet
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> --wallet
# with an explicit signing key
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> --signing-key <KEY>

Asset Transfers

# Native asset transfer
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> --amount 100 --live
# Custom asset transfer
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> \
    --amount 100 \
    --asset-id 0x1234... \
    --live

Gas Configuration

# Set gas price
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> --gas-price 1

# Forward gas to contract
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> --gas-forwarded 1000

# Set maximum fee
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> --max-fee 5000

Common Use Cases

Contract State Queries

# Read contract state
forc call <CONTRACT_ID> --abi <PATH> get_balance

# Query with parameters
forc call <CONTRACT_ID> --abi <PATH> get_user_info 0x1234...

Token Operations

# Check token balance
forc call <CONTRACT_ID> --abi <PATH> balance_of 0x1234...

# Transfer tokens
forc call <CONTRACT_ID> --abi <PATH> transfer 0x1234... 100 --live

Contract Administration

# Check contract owner
forc call <CONTRACT_ID> --abi <PATH> owner

# Update contract parameters
forc call <CONTRACT_ID> --abi <PATH> update_params 42 --live

Tips and Tricks

  • Use --mode simulate to estimate gas costs before making live transactions
  • External contracts are automatically detected (via internal simulations), but can be manually specified with --external-contracts
  • For complex parameter types (tuples, structs, enums), refer to the parameter types table above
  • Always verify contract addresses and ABIs before making live calls
  • Use environment variables for sensitive data like signing keys: SIGNING_KEY=<key>

Troubleshooting

Common issues and solutions

  • ABI Mismatch:

    • Ensure the ABI matches the deployed contract
    • Verify function selectors match exactly
  • Parameter Type Errors:

    • Check parameter formats in the types table
    • Ensure correct number of parameters
  • Network Issues:

    • Verify node connection
    • Check network selection (testnet/mainnet)
  • Transaction Failures:

    • Use simulation mode to debug
    • Check gas settings
    • Verify wallet has sufficient balance

Future Features

The following features are planned for future releases:

  • Support direct transfer of asset(s) to addresses
  • Function signature based calls without ABI
  • Raw calldata input support
  • Function selector completion
  • Enhanced error messages, debugging, and logging (additional verbosity modes)

forc crypto

forc debug

forc doc

forc explore

forc fmt

forc lsp

forc migrate

forc node

Getting Started

Adding Sway Libs as a Dependency

To import any library, the following dependency should be added to the project's Forc.toml file under [dependencies].

sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.24.2" }

For reference, here is a complete Forc.toml file:

[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "MyProject"

[dependencies]
sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.24.2" }

NOTE: Be sure to set the tag to the latest release.

Importing Sway Libs to Your Project

Once Sway Libs is a dependency to your project, you may then import a library in your Sway Smart Contract as so:

use sway_libs::<library>::<library_function>;

For example, to import the only_owner() from the Ownership Library, use the following statement at the top of your Sway file:

use sway_libs::ownership::only_owner;

NOTE: All projects currently use forc 0.66.6, fuels-rs v0.66.6 and fuel-core 0.40.0.

Using Sway Libs

Once the library you require has been imported to your project, you may call or use any functions and structures the library provides.

In the following example, we import the Pausable Library and implement the Pausable ABI with it's associated functions.

use sway_libs::pausable::{_is_paused, _pause, _unpause, Pausable};

// Implement the Pausable ABI for our contract
impl Pausable for Contract {
    #[storage(write)]
    fn pause() {
        _pause(); // Call the provided pause function.
    }

    #[storage(write)]
    fn unpause() {
        _unpause(); // Call the provided unpause function.
    }

    #[storage(read)]
    fn is_paused() -> bool {
        _is_paused() // Call the provided is paused function.
    }
}

Any instructions related to using a specific library should be found within the libraries section of the Sway Libs Book.

For implementation details on the libraries please see the Sway Libs Docs.

Running Tests

There are two sets of tests that should be run: inline tests and sdk-harness tests. Please make sure you are using forc v0.63.3 and fuel-core v0.34.0. You can check what version you are using by running the fuelup show command.

Make sure you are in the source directory of this repository sway-libs/<you are here>.

Run the inline tests:

forc test --path libs --release --locked

Once these tests have passed, run the sdk-harness tests:

forc test --path tests --release --locked && cargo test --manifest-path tests/Cargo.toml

NOTE: This may take a while depending on your hardware, future improvements to Sway will decrease build times. After this has been run once, individual test projects may be built on their own to save time.

Asset Library

The Asset Library provides basic helper functions for the SRC-20; Native Asset Standard, SRC-3; Mint and Burn Standard, and the SRC-7; Arbitrary Asset Metadata Standard. It is intended to make development of Native Assets using Sway quick and easy while following the standard's specifications.

For implementation details on the Asset Library please see the Sway Libs Docs.

SRC-20 Functionality

The Base or core of any Asset on the Fuel Network must follow the SRC-20; Native Asset Standard. The Asset Library's Base section supports the SRC-20's implementation.

SRC-3 Functionality

The SRC-3; Mint and Burn Standard prescribes an ABI for how Native Assets on the Fuel Network are minted and burned. The Asset Library's supply section supports the SRC-3's implementation.

SRC-7 Functionality

The SRC-7; Onchain Asset Metadata Standard prescribes an ABI for stateful metadata associated with Native Assets on the Fuel Network. The Asset Library's metadata section supports the SRC-7's implementation.

Base Functionality

For implementation details on the Asset Library base functionality please see the Sway Libs Docs.

Importing the Asset Library Base Functionality

In order to use the Asset Library, Sway Libs and Sway Standards must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started. To add Sway Standards as a dependency please see the Sway Standards Book.

To import the Asset Library Base Functionality and SRC-20 Standard to your Sway Smart Contract, add the following to your Sway file:

```sway\ncontract;

use std::{hash::*, storage::storage_string::*, string::String};

// ANCHOR: import
use sway_libs::asset::base::*;
use standards::src20::*;
// ANCHOR_END: import

// ANCHOR: src20_abi
abi SRC20 {
    #[storage(read)]
    fn total_assets() -> u64;
    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64>;
    #[storage(read)]
    fn name(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8>;
}
// ANCHOR_END: src20_abi

// ANCHOR: set_attributes
abi SetAssetAttributes {
    #[storage(write)]
    fn set_name(asset: AssetId, name: String);
    #[storage(write)]
    fn set_symbol(asset: AssetId, symbol: String);
    #[storage(write)]
    fn set_decimals(asset: AssetId, decimals: u8);
}
// ANCHOR_END: set_attributes

// ANCHOR: src20_storage
storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
}
// ANCHOR_END: src20_storage\n```

Integration with the SRC-20 Standard

The SRC-20 definition states that the following abi implementation is required for any Native Asset on Fuel:

```sway\ncontract;

use std::{hash::*, storage::storage_string::*, string::String};

// ANCHOR: import
use sway_libs::asset::base::*;
use standards::src20::*;
// ANCHOR_END: import

// ANCHOR: src20_abi
abi SRC20 {
    #[storage(read)]
    fn total_assets() -> u64;
    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64>;
    #[storage(read)]
    fn name(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8>;
}
// ANCHOR_END: src20_abi

// ANCHOR: set_attributes
abi SetAssetAttributes {
    #[storage(write)]
    fn set_name(asset: AssetId, name: String);
    #[storage(write)]
    fn set_symbol(asset: AssetId, symbol: String);
    #[storage(write)]
    fn set_decimals(asset: AssetId, decimals: u8);
}
// ANCHOR_END: set_attributes

// ANCHOR: src20_storage
storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
}
// ANCHOR_END: src20_storage\n```

The Asset Library has the following complimentary functions for each function in the SRC20 abi:

  • _total_assets()
  • _total_supply()
  • _name()
  • _symbol()
  • _decimals()

The following ABI and functions are also provided to set your SRC-20 standard storage values:

```sway\ncontract;

use std::{hash::*, storage::storage_string::*, string::String};

// ANCHOR: import
use sway_libs::asset::base::*;
use standards::src20::*;
// ANCHOR_END: import

// ANCHOR: src20_abi
abi SRC20 {
    #[storage(read)]
    fn total_assets() -> u64;
    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64>;
    #[storage(read)]
    fn name(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8>;
}
// ANCHOR_END: src20_abi

// ANCHOR: set_attributes
abi SetAssetAttributes {
    #[storage(write)]
    fn set_name(asset: AssetId, name: String);
    #[storage(write)]
    fn set_symbol(asset: AssetId, symbol: String);
    #[storage(write)]
    fn set_decimals(asset: AssetId, decimals: u8);
}
// ANCHOR_END: set_attributes

// ANCHOR: src20_storage
storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
}
// ANCHOR_END: src20_storage\n```
  • _set_name()
  • _set_symbol()
  • _set_decimals()

NOTE The _set_name(), _set_symbol(), and _set_decimals() functions will set the attributes of an asset unconditionally. External checks should be applied to restrict the setting of attributes.

Setting Up Storage

Once imported, the Asset Library's base functionality should be available. To use them, be sure to add the storage block below to your contract which enables the SRC-20 standard.

```sway\ncontract;

use std::{hash::*, storage::storage_string::*, string::String};

// ANCHOR: import
use sway_libs::asset::base::*;
use standards::src20::*;
// ANCHOR_END: import

// ANCHOR: src20_abi
abi SRC20 {
    #[storage(read)]
    fn total_assets() -> u64;
    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64>;
    #[storage(read)]
    fn name(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8>;
}
// ANCHOR_END: src20_abi

// ANCHOR: set_attributes
abi SetAssetAttributes {
    #[storage(write)]
    fn set_name(asset: AssetId, name: String);
    #[storage(write)]
    fn set_symbol(asset: AssetId, symbol: String);
    #[storage(write)]
    fn set_decimals(asset: AssetId, decimals: u8);
}
// ANCHOR_END: set_attributes

// ANCHOR: src20_storage
storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
}
// ANCHOR_END: src20_storage\n```

Implementing the SRC-20 Standard with the Asset Library

To use the Asset Library's base functionly, simply pass the StorageKey from the prescribed storage block. The example below shows the implementation of the SRC-20 standard in combination with the Asset Library with no user defined restrictions or custom functionality.

```sway\ncontract;

// ANCHOR: basic_src20
use sway_libs::asset::base::{_decimals, _name, _symbol, _total_assets, _total_supply};
use standards::src20::SRC20;
use std::{hash::Hash, storage::storage_string::*, string::String};

// The SRC-20 storage block
storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
}

// Implement the SRC-20 Standard for this contract
impl SRC20 for Contract {
    #[storage(read)]
    fn total_assets() -> u64 {
        // Pass the `total_assets` StorageKey to `_total_assets()` from the Asset Library.
        _total_assets(storage.total_assets)
    }

    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64> {
        // Pass the `total_supply` StorageKey to `_total_supply()` from the Asset Library.
        _total_supply(storage.total_supply, asset)
    }

    #[storage(read)]
    fn name(asset: AssetId) -> Option<String> {
        // Pass the `name` StorageKey to `_name_()` from the Asset Library.
        _name(storage.name, asset)
    }

    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String> {
        // Pass the `symbol` StorageKey to `_symbol_()` function from the Asset Library.
        _symbol(storage.symbol, asset)
    }

    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8> {
        // Pass the `decimals` StorageKey to `_decimals_()` function from the Asset Library.
        _decimals(storage.decimals, asset)
    }
}
// ANCHOR_END: basic_src20\n```

Setting an Asset's SRC-20 Attributes

To set some the asset attributes for an Asset, use the SetAssetAttributes ABI provided by the Asset Library. The example below shows the implementation of the SetAssetAttributes ABI with no user defined restrictions or custom functionality. It is recommended that the Ownership Library is used in conjunction with the SetAssetAttributes ABI to ensure only a single user has permissions to set an Asset's attributes.

The _set_name(), _set_symbol(), and _set_decimals() functions follows the SRC-20 standard for logging and will emit their respective log when called.

```sway\ncontract;

// ANCHOR: setting_src20_attributes
use sway_libs::asset::base::*;
use std::{hash::Hash, storage::storage_string::*, string::String};

storage {
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
}

impl SetAssetAttributes for Contract {
    #[storage(write)]
    fn set_name(asset: AssetId, name: String) {
        _set_name(storage.name, asset, name);
    }

    #[storage(write)]
    fn set_symbol(asset: AssetId, symbol: String) {
        _set_symbol(storage.symbol, asset, symbol);
    }

    #[storage(write)]
    fn set_decimals(asset: AssetId, decimals: u8) {
        _set_decimals(storage.decimals, asset, decimals);
    }
}
// ANCHOR_END: setting_src20_attributes\n```

NOTE The _set_name(), _set_symbol(), and _set_decimals() functions will set the attributes of an asset unconditionally. External checks should be applied to restrict the setting of attributes.

Supply Functionality

For implementation details on the Asset Library supply functionality please see the Sway Libs Docs.

Importing the Asset Library Supply Functionality

In order to use the Asset Library, Sway Libs and Sway Standards must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started. To add Sway Standards as a dependency please see the Sway Standards Book.

To import the Asset Library Supply Functionality and SRC-3 Standard to your Sway Smart Contract, add the following to your Sway file:

```sway\ncontract;

use std::hash::Hash;

// ANCHOR: import
use sway_libs::asset::supply::*;
use standards::src3::*;
// ANCHOR_END: import

// ANCHOR: src3_abi
abi SRC3 {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64);
    #[payable]
    #[storage(read, write)]
    fn burn(vault_sub_id: SubId, amount: u64);
}
// ANCHOR_END: src3_abi

// ANCHOR: src3_storage
storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
}
// ANCHOR_END: src3_storage\n```

Integration with the SRC-3 Standard

The SRC-3 definition states that the following abi implementation is required for any Native Asset on Fuel which mints and burns tokens:

```sway\ncontract;

use std::hash::Hash;

// ANCHOR: import
use sway_libs::asset::supply::*;
use standards::src3::*;
// ANCHOR_END: import

// ANCHOR: src3_abi
abi SRC3 {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64);
    #[payable]
    #[storage(read, write)]
    fn burn(vault_sub_id: SubId, amount: u64);
}
// ANCHOR_END: src3_abi

// ANCHOR: src3_storage
storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
}
// ANCHOR_END: src3_storage\n```

The Asset Library has the following complimentary functions for each function in the SRC3 abi:

  • _mint()
  • _burn()

NOTE The _mint() and _burn() functions will mint and burn assets unconditionally. External checks should be applied to restrict the minting and burning of assets.

Setting Up Storage

Once imported, the Asset Library's supply functionality should be available. To use them, be sure to add the storage block below to your contract which enables the SRC-3 standard.

```sway\ncontract;

use std::hash::Hash;

// ANCHOR: import
use sway_libs::asset::supply::*;
use standards::src3::*;
// ANCHOR_END: import

// ANCHOR: src3_abi
abi SRC3 {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64);
    #[payable]
    #[storage(read, write)]
    fn burn(vault_sub_id: SubId, amount: u64);
}
// ANCHOR_END: src3_abi

// ANCHOR: src3_storage
storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
}
// ANCHOR_END: src3_storage\n```

Implementing the SRC-3 Standard with the Asset Library

To use either function, simply pass the StorageKey from the prescribed storage block. The example below shows the implementation of the SRC-3 standard in combination with the Asset Library with no user defined restrictions or custom functionality. It is recommended that the Ownership Library is used in conjunction with the Asset Library's supply functionality to ensure only a single user has permissions to mint an Asset.

The _mint() and _burn() functions follows the SRC-20 standard for logging and will emit the TotalSupplyEvent when called.

```sway\ncontract;

use std::hash::*;

// ANCHOR: basic_src3
use sway_libs::asset::supply::{_burn, _mint};
use standards::src3::SRC3;

storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
}

// Implement the SRC-3 Standard for this contract
impl SRC3 for Contract {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64) {
        // Pass the StorageKeys to the `_mint()` function from the Asset Library.
        _mint(
            storage
                .total_assets,
            storage
                .total_supply,
            recipient,
            sub_id
                .unwrap_or(b256::zero()),
            amount,
        );
    }

    // Pass the StorageKeys to the `_burn_()` function from the Asset Library.
    #[payable]
    #[storage(read, write)]
    fn burn(sub_id: SubId, amount: u64) {
        _burn(storage.total_supply, sub_id, amount);
    }
}
// ANCHOR_END: basic_src3\n```

NOTE The _mint() and _burn() functions will mint and burn assets unconditionally. External checks should be applied to restrict the minting and burning of assets.

Metadata Functionality

For implementation details on the Asset Library metadata functionality please see the Sway Libs Docs.

Importing the Asset Library Metadata Functionality

In order to use the Asset Library, Sway Libs and Sway Standards must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started. To add Sway Standards as a dependency please see the Sway Standards Book.

To import the Asset Library Base Functionality and SRC-7 Standard to your Sway Smart Contract, add the following to your Sway file:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: import
use sway_libs::asset::metadata::{_metadata, _set_metadata, SetAssetMetadata, StorageMetadata};
use standards::src7::*;
// ANCHOR_END: import

// ANCHOR: src7_abi
abi SRC7 {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata>;
}
// ANCHOR_END: src7_abi

// ANCHOR: src7_storage
storage {
    metadata: StorageMetadata = StorageMetadata {},
}
// ANCHOR_END: src7_storage

// ANCHOR: src7_metadata_convenience_function
impl SRC7 for Contract {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        _metadata(storage.metadata, asset, key)
    }
}
// ANCHOR_END: src7_metadata_convenience_function

// ANCHOR: src7_set_metadata
impl SetAssetMetadata for Contract {
    #[storage(read, write)]
    fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
        // add your authentication logic here
        // eg. only_owner()
        _set_metadata(storage.metadata, asset, key, metadata);
    }
}
// ANCHOR_END: src7_set_metadata

#[storage(read)]
fn get_metadata(asset: AssetId, key: String) {
    // ANCHOR: get_metadata
    use sway_libs::asset::metadata::*; // To access trait implementations you must import everything using the glob operator.
    let metadata: Option<Metadata> = storage.metadata.get(asset, key);
    // ANCHOR_END: get_metadata

    // ANCHOR: get_metadata_match
    match metadata.unwrap() {
        Metadata::B256(b256) => {
        // do something with b256
},
        Metadata::Bytes(bytes) => {
        // do something with bytes
},
        Metadata::Int(int) => {
        // do something with int
},
        Metadata::String(string) => {
        // do something with string
},
    }
    // ANCHOR_END: get_metadata_match

    // ANCHOR: get_metadata_as
    let metadata: Metadata = storage.metadata.get(asset, key).unwrap();

    if metadata.is_b256() {
        let b256: b256 = metadata.as_b256().unwrap();
        // do something with b256
    } else if metadata.is_bytes() {
        let bytes: Bytes = metadata.as_bytes().unwrap();
        // do something with bytes
    } else if metadata.is_u64() {
        let int: u64 = metadata.as_u64().unwrap();
        // do something with int
    } else if metadata.is_string() {
        let string: String = metadata.as_string().unwrap();
        // do something with string
    }
    // ANCHOR_END: get_metadata_as
}\n```

Integration with the SRC-7 Standard

The SRC-7 definition states that the following abi implementation is required for any Native Asset on Fuel which uses stateful metadata:

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: import
use sway_libs::asset::metadata::{_metadata, _set_metadata, SetAssetMetadata, StorageMetadata};
use standards::src7::*;
// ANCHOR_END: import

// ANCHOR: src7_abi
abi SRC7 {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata>;
}
// ANCHOR_END: src7_abi

// ANCHOR: src7_storage
storage {
    metadata: StorageMetadata = StorageMetadata {},
}
// ANCHOR_END: src7_storage

// ANCHOR: src7_metadata_convenience_function
impl SRC7 for Contract {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        _metadata(storage.metadata, asset, key)
    }
}
// ANCHOR_END: src7_metadata_convenience_function

// ANCHOR: src7_set_metadata
impl SetAssetMetadata for Contract {
    #[storage(read, write)]
    fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
        // add your authentication logic here
        // eg. only_owner()
        _set_metadata(storage.metadata, asset, key, metadata);
    }
}
// ANCHOR_END: src7_set_metadata

#[storage(read)]
fn get_metadata(asset: AssetId, key: String) {
    // ANCHOR: get_metadata
    use sway_libs::asset::metadata::*; // To access trait implementations you must import everything using the glob operator.
    let metadata: Option<Metadata> = storage.metadata.get(asset, key);
    // ANCHOR_END: get_metadata

    // ANCHOR: get_metadata_match
    match metadata.unwrap() {
        Metadata::B256(b256) => {
        // do something with b256
},
        Metadata::Bytes(bytes) => {
        // do something with bytes
},
        Metadata::Int(int) => {
        // do something with int
},
        Metadata::String(string) => {
        // do something with string
},
    }
    // ANCHOR_END: get_metadata_match

    // ANCHOR: get_metadata_as
    let metadata: Metadata = storage.metadata.get(asset, key).unwrap();

    if metadata.is_b256() {
        let b256: b256 = metadata.as_b256().unwrap();
        // do something with b256
    } else if metadata.is_bytes() {
        let bytes: Bytes = metadata.as_bytes().unwrap();
        // do something with bytes
    } else if metadata.is_u64() {
        let int: u64 = metadata.as_u64().unwrap();
        // do something with int
    } else if metadata.is_string() {
        let string: String = metadata.as_string().unwrap();
        // do something with string
    }
    // ANCHOR_END: get_metadata_as
}\n```

The Asset Library has the following complimentary data type for the SRC-7 standard:

  • StorageMetadata

Setting Up Storage

Once imported, the Asset Library's metadata functionality should be available. To use them, be sure to add the storage block below to your contract which enables the SRC-7 standard.

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: import
use sway_libs::asset::metadata::{_metadata, _set_metadata, SetAssetMetadata, StorageMetadata};
use standards::src7::*;
// ANCHOR_END: import

// ANCHOR: src7_abi
abi SRC7 {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata>;
}
// ANCHOR_END: src7_abi

// ANCHOR: src7_storage
storage {
    metadata: StorageMetadata = StorageMetadata {},
}
// ANCHOR_END: src7_storage

// ANCHOR: src7_metadata_convenience_function
impl SRC7 for Contract {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        _metadata(storage.metadata, asset, key)
    }
}
// ANCHOR_END: src7_metadata_convenience_function

// ANCHOR: src7_set_metadata
impl SetAssetMetadata for Contract {
    #[storage(read, write)]
    fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
        // add your authentication logic here
        // eg. only_owner()
        _set_metadata(storage.metadata, asset, key, metadata);
    }
}
// ANCHOR_END: src7_set_metadata

#[storage(read)]
fn get_metadata(asset: AssetId, key: String) {
    // ANCHOR: get_metadata
    use sway_libs::asset::metadata::*; // To access trait implementations you must import everything using the glob operator.
    let metadata: Option<Metadata> = storage.metadata.get(asset, key);
    // ANCHOR_END: get_metadata

    // ANCHOR: get_metadata_match
    match metadata.unwrap() {
        Metadata::B256(b256) => {
        // do something with b256
},
        Metadata::Bytes(bytes) => {
        // do something with bytes
},
        Metadata::Int(int) => {
        // do something with int
},
        Metadata::String(string) => {
        // do something with string
},
    }
    // ANCHOR_END: get_metadata_match

    // ANCHOR: get_metadata_as
    let metadata: Metadata = storage.metadata.get(asset, key).unwrap();

    if metadata.is_b256() {
        let b256: b256 = metadata.as_b256().unwrap();
        // do something with b256
    } else if metadata.is_bytes() {
        let bytes: Bytes = metadata.as_bytes().unwrap();
        // do something with bytes
    } else if metadata.is_u64() {
        let int: u64 = metadata.as_u64().unwrap();
        // do something with int
    } else if metadata.is_string() {
        let string: String = metadata.as_string().unwrap();
        // do something with string
    }
    // ANCHOR_END: get_metadata_as
}\n```

Using the StorageMetadata Type

Setting Metadata

As described in the SRC-7 standard, the metadata type is a simple enum of the following types:

  • b256
  • Bytes
  • u64
  • String

To set some metadata of any of the above types for an Asset, you can use the SetAssetMetadata ABI provided by the Asset Library with the _set_metadata() function. Be sure to follow the SRC-9 standard for your key. It is recommended that the Ownership Library is used in conjunction with the SetAssetMetadata ABI to ensure only a single user has permissions to set an Asset's metadata.

The _set_metadata() function follows the SRC-7 standard for logging and will emit the SetMetadataEvent when called.

```sway\ncontract;

use std::string::String;
use std::bytes::Bytes;

// ANCHOR: setting_src7_attributes
use sway_libs::asset::metadata::*;
use standards::src7::Metadata;

storage {
    metadata: StorageMetadata = StorageMetadata {},
}

impl SetAssetMetadata for Contract {
    #[storage(read, write)]
    fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
        // add your authentication logic here
        // eg. only_owner()
        _set_metadata(storage.metadata, asset, key, metadata);
    }
}
// ANCHOR_END: setting_src7_attributes

// ANCHOR: setting_src7_attributes_custom_abi
abi CustomSetAssetMetadata {
    #[storage(read, write)]
    fn custom_set_metadata(
        asset: AssetId,
        key: String,
        bits256: b256,
        bytes: Bytes,
        int: u64,
        string: String,
    );
}

impl CustomSetAssetMetadata for Contract {
    #[storage(read, write)]
    fn custom_set_metadata(
        asset: AssetId,
        key: String,
        bits256: b256,
        bytes: Bytes,
        int: u64,
        string: String,
    ) {
        let b256_metadata = Metadata::B256(bits256);
        let bytes_metadata = Metadata::Bytes(bytes);
        let int_metadata = Metadata::Int(int);
        let string_metadata = Metadata::String(string);

        // your authentication logic here

        // set whichever metadata you want
        storage.metadata.insert(asset, key, string_metadata);
    }
}
// ANCHOR_END: setting_src7_attributes_custom_abi\n```

NOTE The _set_metadata() function will set the metadata of an asset unconditionally. External checks should be applied to restrict the setting of metadata.

To set the metadata of an Asset, using only one of the above types, you can define a custom ABI and use it as such:

```sway\ncontract;

use std::string::String;
use std::bytes::Bytes;

// ANCHOR: setting_src7_attributes
use sway_libs::asset::metadata::*;
use standards::src7::Metadata;

storage {
    metadata: StorageMetadata = StorageMetadata {},
}

impl SetAssetMetadata for Contract {
    #[storage(read, write)]
    fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
        // add your authentication logic here
        // eg. only_owner()
        _set_metadata(storage.metadata, asset, key, metadata);
    }
}
// ANCHOR_END: setting_src7_attributes

// ANCHOR: setting_src7_attributes_custom_abi
abi CustomSetAssetMetadata {
    #[storage(read, write)]
    fn custom_set_metadata(
        asset: AssetId,
        key: String,
        bits256: b256,
        bytes: Bytes,
        int: u64,
        string: String,
    );
}

impl CustomSetAssetMetadata for Contract {
    #[storage(read, write)]
    fn custom_set_metadata(
        asset: AssetId,
        key: String,
        bits256: b256,
        bytes: Bytes,
        int: u64,
        string: String,
    ) {
        let b256_metadata = Metadata::B256(bits256);
        let bytes_metadata = Metadata::Bytes(bytes);
        let int_metadata = Metadata::Int(int);
        let string_metadata = Metadata::String(string);

        // your authentication logic here

        // set whichever metadata you want
        storage.metadata.insert(asset, key, string_metadata);
    }
}
// ANCHOR_END: setting_src7_attributes_custom_abi\n```

NOTE The _set_metadata() function will set the metadata of an asset unconditionally. External checks should be applied to restrict the setting of metadata.

Implementing the SRC-7 Standard with StorageMetadata

To use the StorageMetadata type, simply get the stored metadata with the associated key and AssetId using the provided _metadata() convenience function. The example below shows the implementation of the SRC-7 standard in combination with the Asset Library's StorageMetadata type and the _metadata() function with no user defined restrictions or custom functionality.

```sway\ncontract;

use std::string::String;

// ANCHOR: basic_src7
use sway_libs::asset::metadata::*;
use standards::src7::{Metadata, SRC7};

storage {
    metadata: StorageMetadata = StorageMetadata {},
}

// Implement the SRC-7 Standard for this contract
impl SRC7 for Contract {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        // Return the stored metadata
        storage.metadata.get(asset, key)
    }
}
// ANCHOR_END: basic_src7\n```

Getting Metadata

To get the metadata for an asset, apart from the above mentioned _metadata() convenience function, you can also use the get() method on the StorageMetadata type, which returns the Metadata type.

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: import
use sway_libs::asset::metadata::{_metadata, _set_metadata, SetAssetMetadata, StorageMetadata};
use standards::src7::*;
// ANCHOR_END: import

// ANCHOR: src7_abi
abi SRC7 {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata>;
}
// ANCHOR_END: src7_abi

// ANCHOR: src7_storage
storage {
    metadata: StorageMetadata = StorageMetadata {},
}
// ANCHOR_END: src7_storage

// ANCHOR: src7_metadata_convenience_function
impl SRC7 for Contract {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        _metadata(storage.metadata, asset, key)
    }
}
// ANCHOR_END: src7_metadata_convenience_function

// ANCHOR: src7_set_metadata
impl SetAssetMetadata for Contract {
    #[storage(read, write)]
    fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
        // add your authentication logic here
        // eg. only_owner()
        _set_metadata(storage.metadata, asset, key, metadata);
    }
}
// ANCHOR_END: src7_set_metadata

#[storage(read)]
fn get_metadata(asset: AssetId, key: String) {
    // ANCHOR: get_metadata
    use sway_libs::asset::metadata::*; // To access trait implementations you must import everything using the glob operator.
    let metadata: Option<Metadata> = storage.metadata.get(asset, key);
    // ANCHOR_END: get_metadata

    // ANCHOR: get_metadata_match
    match metadata.unwrap() {
        Metadata::B256(b256) => {
        // do something with b256
},
        Metadata::Bytes(bytes) => {
        // do something with bytes
},
        Metadata::Int(int) => {
        // do something with int
},
        Metadata::String(string) => {
        // do something with string
},
    }
    // ANCHOR_END: get_metadata_match

    // ANCHOR: get_metadata_as
    let metadata: Metadata = storage.metadata.get(asset, key).unwrap();

    if metadata.is_b256() {
        let b256: b256 = metadata.as_b256().unwrap();
        // do something with b256
    } else if metadata.is_bytes() {
        let bytes: Bytes = metadata.as_bytes().unwrap();
        // do something with bytes
    } else if metadata.is_u64() {
        let int: u64 = metadata.as_u64().unwrap();
        // do something with int
    } else if metadata.is_string() {
        let string: String = metadata.as_string().unwrap();
        // do something with string
    }
    // ANCHOR_END: get_metadata_as
}\n```

This results an Option type as the metadata may not be set for the asset and key combination.

If you know that the metadata is set, but you don't know the type, you can use a match statement to access the metadata.

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: import
use sway_libs::asset::metadata::{_metadata, _set_metadata, SetAssetMetadata, StorageMetadata};
use standards::src7::*;
// ANCHOR_END: import

// ANCHOR: src7_abi
abi SRC7 {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata>;
}
// ANCHOR_END: src7_abi

// ANCHOR: src7_storage
storage {
    metadata: StorageMetadata = StorageMetadata {},
}
// ANCHOR_END: src7_storage

// ANCHOR: src7_metadata_convenience_function
impl SRC7 for Contract {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        _metadata(storage.metadata, asset, key)
    }
}
// ANCHOR_END: src7_metadata_convenience_function

// ANCHOR: src7_set_metadata
impl SetAssetMetadata for Contract {
    #[storage(read, write)]
    fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
        // add your authentication logic here
        // eg. only_owner()
        _set_metadata(storage.metadata, asset, key, metadata);
    }
}
// ANCHOR_END: src7_set_metadata

#[storage(read)]
fn get_metadata(asset: AssetId, key: String) {
    // ANCHOR: get_metadata
    use sway_libs::asset::metadata::*; // To access trait implementations you must import everything using the glob operator.
    let metadata: Option<Metadata> = storage.metadata.get(asset, key);
    // ANCHOR_END: get_metadata

    // ANCHOR: get_metadata_match
    match metadata.unwrap() {
        Metadata::B256(b256) => {
        // do something with b256
},
        Metadata::Bytes(bytes) => {
        // do something with bytes
},
        Metadata::Int(int) => {
        // do something with int
},
        Metadata::String(string) => {
        // do something with string
},
    }
    // ANCHOR_END: get_metadata_match

    // ANCHOR: get_metadata_as
    let metadata: Metadata = storage.metadata.get(asset, key).unwrap();

    if metadata.is_b256() {
        let b256: b256 = metadata.as_b256().unwrap();
        // do something with b256
    } else if metadata.is_bytes() {
        let bytes: Bytes = metadata.as_bytes().unwrap();
        // do something with bytes
    } else if metadata.is_u64() {
        let int: u64 = metadata.as_u64().unwrap();
        // do something with int
    } else if metadata.is_string() {
        let string: String = metadata.as_string().unwrap();
        // do something with string
    }
    // ANCHOR_END: get_metadata_as
}\n```

If you know that the metadata is set and you know the type, you can use the as_* methods to access the metadata. We also provide is_* methods to check if the metadata is of a specific type.

```sway\ncontract;

use std::{bytes::Bytes, string::String};

// ANCHOR: import
use sway_libs::asset::metadata::{_metadata, _set_metadata, SetAssetMetadata, StorageMetadata};
use standards::src7::*;
// ANCHOR_END: import

// ANCHOR: src7_abi
abi SRC7 {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata>;
}
// ANCHOR_END: src7_abi

// ANCHOR: src7_storage
storage {
    metadata: StorageMetadata = StorageMetadata {},
}
// ANCHOR_END: src7_storage

// ANCHOR: src7_metadata_convenience_function
impl SRC7 for Contract {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        _metadata(storage.metadata, asset, key)
    }
}
// ANCHOR_END: src7_metadata_convenience_function

// ANCHOR: src7_set_metadata
impl SetAssetMetadata for Contract {
    #[storage(read, write)]
    fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
        // add your authentication logic here
        // eg. only_owner()
        _set_metadata(storage.metadata, asset, key, metadata);
    }
}
// ANCHOR_END: src7_set_metadata

#[storage(read)]
fn get_metadata(asset: AssetId, key: String) {
    // ANCHOR: get_metadata
    use sway_libs::asset::metadata::*; // To access trait implementations you must import everything using the glob operator.
    let metadata: Option<Metadata> = storage.metadata.get(asset, key);
    // ANCHOR_END: get_metadata

    // ANCHOR: get_metadata_match
    match metadata.unwrap() {
        Metadata::B256(b256) => {
        // do something with b256
},
        Metadata::Bytes(bytes) => {
        // do something with bytes
},
        Metadata::Int(int) => {
        // do something with int
},
        Metadata::String(string) => {
        // do something with string
},
    }
    // ANCHOR_END: get_metadata_match

    // ANCHOR: get_metadata_as
    let metadata: Metadata = storage.metadata.get(asset, key).unwrap();

    if metadata.is_b256() {
        let b256: b256 = metadata.as_b256().unwrap();
        // do something with b256
    } else if metadata.is_bytes() {
        let bytes: Bytes = metadata.as_bytes().unwrap();
        // do something with bytes
    } else if metadata.is_u64() {
        let int: u64 = metadata.as_u64().unwrap();
        // do something with int
    } else if metadata.is_string() {
        let string: String = metadata.as_string().unwrap();
        // do something with string
    }
    // ANCHOR_END: get_metadata_as
}\n```

Admin Library

The Admin library provides a way to block users without an "administrative status" from calling functions within a contract. The Admin Library differs from the Ownership Library as multiple users may have administrative status. The Admin Library is often used when needing administrative calls on a contract that involve multiple users or a whitelist.

This library extends the Ownership Library. The Ownership library must be imported and used to enable the Admin library. Only the contract's owner may add and remove administrative users.

For implementation details on the Admin Library please see the Sway Libs Docs.

Importing the Admin Library

In order to use the Admin Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Admin Library, be sure to include both the Admin and Ownership Libraries in your import statements.

```sway\nlibrary;

mod owner_integration;

// ANCHOR: import
use sway_libs::{admin::*, ownership::*};
// ANCHOR_END: import

// ANCHOR: add_admin
#[storage(read, write)]
fn add_a_admin(new_admin: Identity) {
    // Can only be called by contract's owner.
    add_admin(new_admin);
}
// ANCHOR_END: add_admin

// ANCHOR: remove_admin
#[storage(read, write)]
fn remove_an_admin(old_admin: Identity) {
    // Can only be called by contract's owner.
    revoke_admin(old_admin);
}
// ANCHOR_END: remove_admin

// ANCHOR: only_admin
#[storage(read)]
fn only_owner_may_call() {
    only_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: only_admin

// ANCHOR: both_admin
#[storage(read)]
fn both_owner_or_admin_may_call() {
    only_owner_or_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: both_admin

// ANCHOR: check_admin
#[storage(read)]
fn check_if_admin(admin: Identity) {
    let status = is_admin(admin);
    assert(status);
}
// ANCHOR_END: check_admin\n```

Integrating the Admin Library into the Ownership Library

To use the Admin library, be sure to set a contract owner for your contract. The following demonstrates setting a contract owner using the Ownership Library.

```sway\nlibrary;

// ANCHOR: ownership_integration
use sway_libs::{admin::add_admin, ownership::initialize_ownership};

#[storage(read, write)]
fn my_constructor(new_owner: Identity) {
    initialize_ownership(new_owner);
}

#[storage(read, write)]
fn add_a_admin(new_admin: Identity) {
    // Can only be called by contract's owner set in the constructor above.
    add_admin(new_admin);
}
// ANCHOR_END: ownership_integration\n```

Basic Functionality

Adding an Admin

To add a new admin to a contract, call the add_admin() function.

```sway\nlibrary;

mod owner_integration;

// ANCHOR: import
use sway_libs::{admin::*, ownership::*};
// ANCHOR_END: import

// ANCHOR: add_admin
#[storage(read, write)]
fn add_a_admin(new_admin: Identity) {
    // Can only be called by contract's owner.
    add_admin(new_admin);
}
// ANCHOR_END: add_admin

// ANCHOR: remove_admin
#[storage(read, write)]
fn remove_an_admin(old_admin: Identity) {
    // Can only be called by contract's owner.
    revoke_admin(old_admin);
}
// ANCHOR_END: remove_admin

// ANCHOR: only_admin
#[storage(read)]
fn only_owner_may_call() {
    only_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: only_admin

// ANCHOR: both_admin
#[storage(read)]
fn both_owner_or_admin_may_call() {
    only_owner_or_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: both_admin

// ANCHOR: check_admin
#[storage(read)]
fn check_if_admin(admin: Identity) {
    let status = is_admin(admin);
    assert(status);
}
// ANCHOR_END: check_admin\n```

NOTE Only the contract's owner may call this function. Please see the example above to set a contract owner.

Removing an Admin

To remove an admin from a contract, call the revoke_admin() function.

```sway\nlibrary;

mod owner_integration;

// ANCHOR: import
use sway_libs::{admin::*, ownership::*};
// ANCHOR_END: import

// ANCHOR: add_admin
#[storage(read, write)]
fn add_a_admin(new_admin: Identity) {
    // Can only be called by contract's owner.
    add_admin(new_admin);
}
// ANCHOR_END: add_admin

// ANCHOR: remove_admin
#[storage(read, write)]
fn remove_an_admin(old_admin: Identity) {
    // Can only be called by contract's owner.
    revoke_admin(old_admin);
}
// ANCHOR_END: remove_admin

// ANCHOR: only_admin
#[storage(read)]
fn only_owner_may_call() {
    only_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: only_admin

// ANCHOR: both_admin
#[storage(read)]
fn both_owner_or_admin_may_call() {
    only_owner_or_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: both_admin

// ANCHOR: check_admin
#[storage(read)]
fn check_if_admin(admin: Identity) {
    let status = is_admin(admin);
    assert(status);
}
// ANCHOR_END: check_admin\n```

NOTE Only the contract's owner may call this function. Please see the example above to set a contract owner.

Applying Restrictions

To restrict a function to only an admin, call the only_admin() function.

```sway\nlibrary;

mod owner_integration;

// ANCHOR: import
use sway_libs::{admin::*, ownership::*};
// ANCHOR_END: import

// ANCHOR: add_admin
#[storage(read, write)]
fn add_a_admin(new_admin: Identity) {
    // Can only be called by contract's owner.
    add_admin(new_admin);
}
// ANCHOR_END: add_admin

// ANCHOR: remove_admin
#[storage(read, write)]
fn remove_an_admin(old_admin: Identity) {
    // Can only be called by contract's owner.
    revoke_admin(old_admin);
}
// ANCHOR_END: remove_admin

// ANCHOR: only_admin
#[storage(read)]
fn only_owner_may_call() {
    only_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: only_admin

// ANCHOR: both_admin
#[storage(read)]
fn both_owner_or_admin_may_call() {
    only_owner_or_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: both_admin

// ANCHOR: check_admin
#[storage(read)]
fn check_if_admin(admin: Identity) {
    let status = is_admin(admin);
    assert(status);
}
// ANCHOR_END: check_admin\n```

NOTE: Admins and the contract's owner are independent of one another. only_admin() will revert if called by the contract's owner.

To restrict a function to only an admin or the contract's owner, call the only_owner_or_admin() function.

```sway\nlibrary;

mod owner_integration;

// ANCHOR: import
use sway_libs::{admin::*, ownership::*};
// ANCHOR_END: import

// ANCHOR: add_admin
#[storage(read, write)]
fn add_a_admin(new_admin: Identity) {
    // Can only be called by contract's owner.
    add_admin(new_admin);
}
// ANCHOR_END: add_admin

// ANCHOR: remove_admin
#[storage(read, write)]
fn remove_an_admin(old_admin: Identity) {
    // Can only be called by contract's owner.
    revoke_admin(old_admin);
}
// ANCHOR_END: remove_admin

// ANCHOR: only_admin
#[storage(read)]
fn only_owner_may_call() {
    only_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: only_admin

// ANCHOR: both_admin
#[storage(read)]
fn both_owner_or_admin_may_call() {
    only_owner_or_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: both_admin

// ANCHOR: check_admin
#[storage(read)]
fn check_if_admin(admin: Identity) {
    let status = is_admin(admin);
    assert(status);
}
// ANCHOR_END: check_admin\n```

Checking Admin Status

To check the administrative privileges of a user, call the is_admin() function.

```sway\nlibrary;

mod owner_integration;

// ANCHOR: import
use sway_libs::{admin::*, ownership::*};
// ANCHOR_END: import

// ANCHOR: add_admin
#[storage(read, write)]
fn add_a_admin(new_admin: Identity) {
    // Can only be called by contract's owner.
    add_admin(new_admin);
}
// ANCHOR_END: add_admin

// ANCHOR: remove_admin
#[storage(read, write)]
fn remove_an_admin(old_admin: Identity) {
    // Can only be called by contract's owner.
    revoke_admin(old_admin);
}
// ANCHOR_END: remove_admin

// ANCHOR: only_admin
#[storage(read)]
fn only_owner_may_call() {
    only_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: only_admin

// ANCHOR: both_admin
#[storage(read)]
fn both_owner_or_admin_may_call() {
    only_owner_or_admin();
    // Only an admin may reach this line.
}
// ANCHOR_END: both_admin

// ANCHOR: check_admin
#[storage(read)]
fn check_if_admin(admin: Identity) {
    let status = is_admin(admin);
    assert(status);
}
// ANCHOR_END: check_admin\n```

Ownership Library

The Ownership Library provides a straightforward way to restrict specific calls in a Sway contract to a single owner. Its design follows the SRC-5 standard from Sway Standards and offers a set of functions to initialize, verify, revoke, and transfer ownership.

For implementation details, visit the Sway Libs Docs.

Importing the Ownership Library

  1. Add Sway Libs to Forc.toml
    Please see the Getting Started guide for instructions on adding Sway Libs as a dependency.

  2. Add Sway Standards to Forc.toml
    Refer to the Sway Standards Book to add Sway Standards.

  3. Import the Ownership Library
    To import the Ownership Library and the SRC-5 standard, include the following in your Sway file:

    ```sway\nlibrary;
    
    

// ANCHOR: import use sway_libs::ownership::; use standards::src5::; // ANCHOR_END: import

// ANCHOR: integrate_with_src5 use sway_libs::ownership::_owner; use standards::src5::{SRC5, State};

impl SRC5 for Contract { #[storage(read)] fn owner() -> State { _owner() } } // ANCHOR_END: integrate_with_src5

// ANCHOR: initialize #[storage(read, write)] fn my_constructor(new_owner: Identity) { initialize_ownership(new_owner); } // ANCHOR_END: initialize

// ANCHOR: only_owner #[storage(read)] fn only_owner_may_call() { only_owner(); // Only the contract's owner may reach this line. } // ANCHOR_END: only_owner

// ANCHOR: state #[storage(read)] fn get_owner_state() { let owner: State = _owner(); } // ANCHOR_END: state

// ANCHOR: transfer_ownership #[storage(read, write)] fn transfer_contract_ownership(new_owner: Identity) { // The caller must be the current owner. transfer_ownership(new_owner); } // ANCHOR: transfer_ownership

// ANCHOR: renouncing_ownership #[storage(read, write)] fn renounce_contract_owner() { // The caller must be the current owner. renounce_ownership(); // Now no one owns the contract. } // ANCHOR: renouncing_ownership\n```


## Integrating the Ownership Library into the SRC-5 Standard

When integrating the Ownership Library with [SRC-5](https://docs.fuel.network/docs/sway-standards/src-5-ownership/), ensure that the `SRC5` trait from **Sway Standards** is implemented in your contract, as shown below. The `_owner()` function from this library is used to fulfill the SRC-5 requirement of exposing the ownership state.

```sway
```sway\nlibrary;

// ANCHOR: import
use sway_libs::ownership::*;
use standards::src5::*;
// ANCHOR_END: import

// ANCHOR: integrate_with_src5
use sway_libs::ownership::_owner;
use standards::src5::{SRC5, State};

impl SRC5 for Contract {
 #[storage(read)]
 fn owner() -> State {
     _owner()
 }
}
// ANCHOR_END: integrate_with_src5

// ANCHOR: initialize
#[storage(read, write)]
fn my_constructor(new_owner: Identity) {
 initialize_ownership(new_owner);
}
// ANCHOR_END: initialize

// ANCHOR: only_owner
#[storage(read)]
fn only_owner_may_call() {
 only_owner();
 // Only the contract's owner may reach this line.
}
// ANCHOR_END: only_owner

// ANCHOR: state
#[storage(read)]
fn get_owner_state() {
 let owner: State = _owner();
}
// ANCHOR_END: state

// ANCHOR: transfer_ownership
#[storage(read, write)]
fn transfer_contract_ownership(new_owner: Identity) {
 // The caller must be the current owner.
 transfer_ownership(new_owner);
}
// ANCHOR: transfer_ownership

// ANCHOR: renouncing_ownership
#[storage(read, write)]
fn renounce_contract_owner() {
 // The caller must be the current owner.
 renounce_ownership();
 // Now no one owns the contract.
}
// ANCHOR: renouncing_ownership\n```

Basic Usage

Setting a Contract Owner

Establishes the initial ownership state by calling initialize_ownership(new_owner). This can only be done once, typically in your contract's constructor.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::ownership::*;
use standards::src5::*;
// ANCHOR_END: import

// ANCHOR: integrate_with_src5
use sway_libs::ownership::_owner;
use standards::src5::{SRC5, State};

impl SRC5 for Contract {
    #[storage(read)]
    fn owner() -> State {
        _owner()
    }
}
// ANCHOR_END: integrate_with_src5

// ANCHOR: initialize
#[storage(read, write)]
fn my_constructor(new_owner: Identity) {
    initialize_ownership(new_owner);
}
// ANCHOR_END: initialize

// ANCHOR: only_owner
#[storage(read)]
fn only_owner_may_call() {
    only_owner();
    // Only the contract's owner may reach this line.
}
// ANCHOR_END: only_owner

// ANCHOR: state
#[storage(read)]
fn get_owner_state() {
    let owner: State = _owner();
}
// ANCHOR_END: state

// ANCHOR: transfer_ownership
#[storage(read, write)]
fn transfer_contract_ownership(new_owner: Identity) {
    // The caller must be the current owner.
    transfer_ownership(new_owner);
}
// ANCHOR: transfer_ownership

// ANCHOR: renouncing_ownership
#[storage(read, write)]
fn renounce_contract_owner() {
    // The caller must be the current owner.
    renounce_ownership();
    // Now no one owns the contract.
}
// ANCHOR: renouncing_ownership\n```

Applying Restrictions

Protect functions so only the owner can call them by invoking only_owner() at the start of those functions.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::ownership::*;
use standards::src5::*;
// ANCHOR_END: import

// ANCHOR: integrate_with_src5
use sway_libs::ownership::_owner;
use standards::src5::{SRC5, State};

impl SRC5 for Contract {
    #[storage(read)]
    fn owner() -> State {
        _owner()
    }
}
// ANCHOR_END: integrate_with_src5

// ANCHOR: initialize
#[storage(read, write)]
fn my_constructor(new_owner: Identity) {
    initialize_ownership(new_owner);
}
// ANCHOR_END: initialize

// ANCHOR: only_owner
#[storage(read)]
fn only_owner_may_call() {
    only_owner();
    // Only the contract's owner may reach this line.
}
// ANCHOR_END: only_owner

// ANCHOR: state
#[storage(read)]
fn get_owner_state() {
    let owner: State = _owner();
}
// ANCHOR_END: state

// ANCHOR: transfer_ownership
#[storage(read, write)]
fn transfer_contract_ownership(new_owner: Identity) {
    // The caller must be the current owner.
    transfer_ownership(new_owner);
}
// ANCHOR: transfer_ownership

// ANCHOR: renouncing_ownership
#[storage(read, write)]
fn renounce_contract_owner() {
    // The caller must be the current owner.
    renounce_ownership();
    // Now no one owns the contract.
}
// ANCHOR: renouncing_ownership\n```

Checking the Ownership Status

To retrieve the current ownership state, call _owner().

```sway\nlibrary;

// ANCHOR: import
use sway_libs::ownership::*;
use standards::src5::*;
// ANCHOR_END: import

// ANCHOR: integrate_with_src5
use sway_libs::ownership::_owner;
use standards::src5::{SRC5, State};

impl SRC5 for Contract {
    #[storage(read)]
    fn owner() -> State {
        _owner()
    }
}
// ANCHOR_END: integrate_with_src5

// ANCHOR: initialize
#[storage(read, write)]
fn my_constructor(new_owner: Identity) {
    initialize_ownership(new_owner);
}
// ANCHOR_END: initialize

// ANCHOR: only_owner
#[storage(read)]
fn only_owner_may_call() {
    only_owner();
    // Only the contract's owner may reach this line.
}
// ANCHOR_END: only_owner

// ANCHOR: state
#[storage(read)]
fn get_owner_state() {
    let owner: State = _owner();
}
// ANCHOR_END: state

// ANCHOR: transfer_ownership
#[storage(read, write)]
fn transfer_contract_ownership(new_owner: Identity) {
    // The caller must be the current owner.
    transfer_ownership(new_owner);
}
// ANCHOR: transfer_ownership

// ANCHOR: renouncing_ownership
#[storage(read, write)]
fn renounce_contract_owner() {
    // The caller must be the current owner.
    renounce_ownership();
    // Now no one owns the contract.
}
// ANCHOR: renouncing_ownership\n```

Transferring Ownership

To transfer ownership from the current owner to a new owner, call transfer_ownership(new_owner).

```sway\nlibrary;

// ANCHOR: import
use sway_libs::ownership::*;
use standards::src5::*;
// ANCHOR_END: import

// ANCHOR: integrate_with_src5
use sway_libs::ownership::_owner;
use standards::src5::{SRC5, State};

impl SRC5 for Contract {
    #[storage(read)]
    fn owner() -> State {
        _owner()
    }
}
// ANCHOR_END: integrate_with_src5

// ANCHOR: initialize
#[storage(read, write)]
fn my_constructor(new_owner: Identity) {
    initialize_ownership(new_owner);
}
// ANCHOR_END: initialize

// ANCHOR: only_owner
#[storage(read)]
fn only_owner_may_call() {
    only_owner();
    // Only the contract's owner may reach this line.
}
// ANCHOR_END: only_owner

// ANCHOR: state
#[storage(read)]
fn get_owner_state() {
    let owner: State = _owner();
}
// ANCHOR_END: state

// ANCHOR: transfer_ownership
#[storage(read, write)]
fn transfer_contract_ownership(new_owner: Identity) {
    // The caller must be the current owner.
    transfer_ownership(new_owner);
}
// ANCHOR: transfer_ownership

// ANCHOR: renouncing_ownership
#[storage(read, write)]
fn renounce_contract_owner() {
    // The caller must be the current owner.
    renounce_ownership();
    // Now no one owns the contract.
}
// ANCHOR: renouncing_ownership\n```

Renouncing Ownership

To revoke ownership entirely and disallow the assignment of a new owner, call renounce_ownership().

```sway\nlibrary;

// ANCHOR: import
use sway_libs::ownership::*;
use standards::src5::*;
// ANCHOR_END: import

// ANCHOR: integrate_with_src5
use sway_libs::ownership::_owner;
use standards::src5::{SRC5, State};

impl SRC5 for Contract {
    #[storage(read)]
    fn owner() -> State {
        _owner()
    }
}
// ANCHOR_END: integrate_with_src5

// ANCHOR: initialize
#[storage(read, write)]
fn my_constructor(new_owner: Identity) {
    initialize_ownership(new_owner);
}
// ANCHOR_END: initialize

// ANCHOR: only_owner
#[storage(read)]
fn only_owner_may_call() {
    only_owner();
    // Only the contract's owner may reach this line.
}
// ANCHOR_END: only_owner

// ANCHOR: state
#[storage(read)]
fn get_owner_state() {
    let owner: State = _owner();
}
// ANCHOR_END: state

// ANCHOR: transfer_ownership
#[storage(read, write)]
fn transfer_contract_ownership(new_owner: Identity) {
    // The caller must be the current owner.
    transfer_ownership(new_owner);
}
// ANCHOR: transfer_ownership

// ANCHOR: renouncing_ownership
#[storage(read, write)]
fn renounce_contract_owner() {
    // The caller must be the current owner.
    renounce_ownership();
    // Now no one owns the contract.
}
// ANCHOR: renouncing_ownership\n```

Events

OwnershipRenounced

Emitted when ownership is revoked.

  • Fields:
    • previous_owner: Identity of the owner prior to revocation.

OwnershipSet

Emitted when initial ownership is set.

  • Fields:
    • new_owner: Identity of the newly set owner.

OwnershipTransferred

Emitted when ownership is transferred from one owner to another.

  • Fields:
    • new_owner: Identity of the new owner.
    • previous_owner: Identity of the prior owner.

Errors

InitializationError

  • Variants:
    • CannotReinitialized: Thrown when attempting to initialize ownership if the owner is already set.

AccessError

  • Variants:
    • NotOwner: Thrown when a function restricted to the owner is called by a non-owner.

Example Integration

Below is a example illustrating how to use this library within a Sway contract:

```sway\n// ANCHOR: example_contract
contract;

use sway_libs::ownership::{
    _owner,
    initialize_ownership,
    only_owner,
    renounce_ownership,
    transfer_ownership,
};
use standards::src5::{SRC5, State};

impl SRC5 for Contract {
    #[storage(read)]
    fn owner() -> State {
        _owner()
    }
}

abi MyContract {
    #[storage(read, write)]
    fn constructor(new_owner: Identity);
    #[storage(read)]
    fn restricted_action();
    #[storage(read, write)]
    fn change_owner(new_owner: Identity);
    #[storage(read, write)]
    fn revoke_ownership();
    #[storage(read)]
    fn get_current_owner() -> State;
}

impl MyContract for Contract {
    #[storage(read, write)]
    fn constructor(new_owner: Identity) {
        initialize_ownership(new_owner);
    }

    // A function restricted to the owner
    #[storage(read)]
    fn restricted_action() {
        only_owner();
        // Protected action
    }

    // Transfer ownership
    #[storage(read, write)]
    fn change_owner(new_owner: Identity) {
        transfer_ownership(new_owner);
    }

    // Renounce ownership
    #[storage(read, write)]
    fn revoke_ownership() {
        renounce_ownership();
    }

    // Get current owner state
    #[storage(read)]
    fn get_current_owner() -> State {
        _owner()
    }
}
// ANCHOR: example_contract\n```
  1. Initialization: Call constructor(new_owner) once to set the initial owner.
  2. Restricted Calls: Use only_owner() to guard any owner-specific functions.
  3. Ownership Checks: Retrieve the current owner state via _owner().
  4. Transfer or Renounce: Use transfer_ownership(new_owner) or renounce_ownership() for ownership modifications.

Pausable Library

The Pausable library allows contracts to implement an emergency stop mechanism. This can be useful for scenarios such as having an emergency switch to freeze all transactions in the event of a large bug.

It is highly encouraged to use the Ownership Library in combination with the Pausable Library to ensure that only a single administrative user has the ability to pause your contract.

For implementation details on the Pausable Library please see the Sway Libs Docs.

Importing the Pausable Library

In order to use the Pausable library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Pausable Library to your Sway Smart Contract, add the following to your Sway file:

```sway\ncontract;

// ANCHOR: import
use sway_libs::pausable::*;
// ANCHOR_END: import

// ANCHOR: pausable_impl
use sway_libs::pausable::{_is_paused, _pause, _unpause, Pausable};

impl Pausable for Contract {
    #[storage(write)]
    fn pause() {
        _pause();
    }

    #[storage(write)]
    fn unpause() {
        _unpause();
    }

    #[storage(read)]
    fn is_paused() -> bool {
        _is_paused()
    }
}
// ANCHOR_END: pausable_impl

// ANCHOR: require_paused
use sway_libs::pausable::require_paused;

#[storage(read)]
fn require_paused_state() {
    require_paused();
    // This comment will only ever be reached if the contract is in the paused state
}
// ANCHOR_END: require_paused

// ANCHOR: require_not_paused
use sway_libs::pausable::require_not_paused;

#[storage(read)]
fn require_not_paused_state() {
    require_not_paused();
    // This comment will only ever be reached if the contract is in the unpaused state
}
// ANCHOR_END: require_not_paused\n```

Basic Functionality

Implementing the Pausable abi

The Pausable Library has two states:

  • Paused
  • Unpaused

By default, your contract will start in the Unpaused state. To pause your contract, you may call the _pause() function. The example below provides a basic pausable contract using the Pausable Library's Pausable abi without any restrictions such as an administrator.

```sway\ncontract;

// ANCHOR: import
use sway_libs::pausable::*;
// ANCHOR_END: import

// ANCHOR: pausable_impl
use sway_libs::pausable::{_is_paused, _pause, _unpause, Pausable};

impl Pausable for Contract {
    #[storage(write)]
    fn pause() {
        _pause();
    }

    #[storage(write)]
    fn unpause() {
        _unpause();
    }

    #[storage(read)]
    fn is_paused() -> bool {
        _is_paused()
    }
}
// ANCHOR_END: pausable_impl

// ANCHOR: require_paused
use sway_libs::pausable::require_paused;

#[storage(read)]
fn require_paused_state() {
    require_paused();
    // This comment will only ever be reached if the contract is in the paused state
}
// ANCHOR_END: require_paused

// ANCHOR: require_not_paused
use sway_libs::pausable::require_not_paused;

#[storage(read)]
fn require_not_paused_state() {
    require_not_paused();
    // This comment will only ever be reached if the contract is in the unpaused state
}
// ANCHOR_END: require_not_paused\n```

Applying Paused Restrictions

When developing a contract, you may want to lock functions down to a specific state. To do this, you may call either of the require_paused() or require_not_paused() functions. The example below shows these functions in use.

```sway\ncontract;

// ANCHOR: import
use sway_libs::pausable::*;
// ANCHOR_END: import

// ANCHOR: pausable_impl
use sway_libs::pausable::{_is_paused, _pause, _unpause, Pausable};

impl Pausable for Contract {
    #[storage(write)]
    fn pause() {
        _pause();
    }

    #[storage(write)]
    fn unpause() {
        _unpause();
    }

    #[storage(read)]
    fn is_paused() -> bool {
        _is_paused()
    }
}
// ANCHOR_END: pausable_impl

// ANCHOR: require_paused
use sway_libs::pausable::require_paused;

#[storage(read)]
fn require_paused_state() {
    require_paused();
    // This comment will only ever be reached if the contract is in the paused state
}
// ANCHOR_END: require_paused

// ANCHOR: require_not_paused
use sway_libs::pausable::require_not_paused;

#[storage(read)]
fn require_not_paused_state() {
    require_not_paused();
    // This comment will only ever be reached if the contract is in the unpaused state
}
// ANCHOR_END: require_not_paused\n```
```sway\ncontract;

// ANCHOR: import
use sway_libs::pausable::*;
// ANCHOR_END: import

// ANCHOR: pausable_impl
use sway_libs::pausable::{_is_paused, _pause, _unpause, Pausable};

impl Pausable for Contract {
    #[storage(write)]
    fn pause() {
        _pause();
    }

    #[storage(write)]
    fn unpause() {
        _unpause();
    }

    #[storage(read)]
    fn is_paused() -> bool {
        _is_paused()
    }
}
// ANCHOR_END: pausable_impl

// ANCHOR: require_paused
use sway_libs::pausable::require_paused;

#[storage(read)]
fn require_paused_state() {
    require_paused();
    // This comment will only ever be reached if the contract is in the paused state
}
// ANCHOR_END: require_paused

// ANCHOR: require_not_paused
use sway_libs::pausable::require_not_paused;

#[storage(read)]
fn require_not_paused_state() {
    require_not_paused();
    // This comment will only ever be reached if the contract is in the unpaused state
}
// ANCHOR_END: require_not_paused\n```

Using the Ownership Library with the Pausable Library

It is highly recommended to integrate the Ownership Library with the Pausable Library and apply restrictions the pause() and unpause() functions. This will ensure that only a single user may pause and unpause a contract in cause of emergency. Failure to apply this restriction will allow any user to obstruct a contract's functionality.

The follow example implements the Pausable abi and applies restrictions to it's pause/unpause functions. The owner of the contract must be set in a constructor defined by MyConstructor in this example.

```sway\ncontract;

// ANCHOR: impl_with_ownership
use sway_libs::{
    ownership::{
        initialize_ownership,
        only_owner,
    },
    pausable::{
        _is_paused,
        _pause,
        _unpause,
        Pausable,
    },
};

abi MyConstructor {
    #[storage(read, write)]
    fn my_constructor(new_owner: Identity);
}

impl MyConstructor for Contract {
    #[storage(read, write)]
    fn my_constructor(new_owner: Identity) {
        initialize_ownership(new_owner);
    }
}

impl Pausable for Contract {
    #[storage(write)]
    fn pause() {
        // Add the `only_owner()` check to ensure only the owner may unpause this contract.
        only_owner();
        _pause();
    }

    #[storage(write)]
    fn unpause() {
        // Add the `only_owner()` check to ensure only the owner may unpause this contract.
        only_owner();
        _unpause();
    }

    #[storage(read)]
    fn is_paused() -> bool {
        _is_paused()
    }
}
// ANCHOR_END: impl_with_ownership\n```

Reentrancy Guard Library

The Reentrancy Guard Library provides an API to check for and disallow reentrancy on a contract. A reentrancy attack happens when a function is externally invoked during its execution, allowing it to be run multiple times in a single transaction.

The reentrancy check is used to check if a contract ID has been called more than once in the current call stack.

A reentrancy, or "recursive call" attack can cause some functions to behave in unexpected ways. This can be prevented by asserting a contract has not yet been called in the current transaction. An example can be found here.

For implementation details on the Reentrancy Guard Library please see the Sway Libs Docs.

Importing the Reentrancy Guard Library

In order to use the Reentrancy Guard library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Reentrancy Guard Library to your Sway Smart Contract, add the following to your Sway file:

```sway\ncontract;

// ANCHOR: import
use sway_libs::reentrancy::*;
// ANCHOR_END: import

// ANCHOR: reentrancy_guard
use sway_libs::reentrancy::reentrancy_guard;

abi MyContract {
    fn my_non_reentrant_function();
}

impl MyContract for Contract {
    fn my_non_reentrant_function() {
        reentrancy_guard();

        // my code here
    }
}
// ANCHOR_END: reentrancy_guard

// ANCHOR: is_reentrant
use sway_libs::reentrancy::is_reentrant;

fn check_if_reentrant() {
    assert(!is_reentrant());
}
// ANCHOR_END: is_reentrant\n```

Basic Functionality

Once imported, using the Reentrancy Library can be done by calling one of the two functions:

  • is_reentrant() -> bool
  • reentrancy_guard()

Using the Reentrancy Guard

Once imported, using the Reentrancy Guard Library can be used by calling the reentrancy_guard() in your Sway Smart Contract. The following shows a Sway Smart Contract that applies the Reentrancy Guard Library:

```sway\ncontract;

// ANCHOR: import
use sway_libs::reentrancy::*;
// ANCHOR_END: import

// ANCHOR: reentrancy_guard
use sway_libs::reentrancy::reentrancy_guard;

abi MyContract {
    fn my_non_reentrant_function();
}

impl MyContract for Contract {
    fn my_non_reentrant_function() {
        reentrancy_guard();

        // my code here
    }
}
// ANCHOR_END: reentrancy_guard

// ANCHOR: is_reentrant
use sway_libs::reentrancy::is_reentrant;

fn check_if_reentrant() {
    assert(!is_reentrant());
}
// ANCHOR_END: is_reentrant\n```

Checking Reentrancy Status

To check if the current caller is a reentrant, you may call the is_reentrant() function.

```sway\ncontract;

// ANCHOR: import
use sway_libs::reentrancy::*;
// ANCHOR_END: import

// ANCHOR: reentrancy_guard
use sway_libs::reentrancy::reentrancy_guard;

abi MyContract {
    fn my_non_reentrant_function();
}

impl MyContract for Contract {
    fn my_non_reentrant_function() {
        reentrancy_guard();

        // my code here
    }
}
// ANCHOR_END: reentrancy_guard

// ANCHOR: is_reentrant
use sway_libs::reentrancy::is_reentrant;

fn check_if_reentrant() {
    assert(!is_reentrant());
}
// ANCHOR_END: is_reentrant\n```

Cross Contract Reentrancy

Cross-Contract Reentrancy is not possible on Fuel due to the use of Native Assets. As such, no contract calls are performed when assets are transferred. However standard security practices when relying on other contracts for state should still be applied, especially when making external calls.

Bytecode Library

The Bytecode Library allows for on-chain verification and computation of bytecode roots for contracts and predicates.

A bytecode root for a contract and predicate is the Merkle root of the binary Merkle tree with each leaf being 16KiB of instructions. This library will compute any contract's or predicate's bytecode root/address allowing for the verification of deployed contracts and generation of predicate addresses on-chain.

For implementation details on the Bytecode Library please see the Sway Libs Docs.

Importing the Bytecode Library

In order to use the Bytecode Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Bytecode Library to your Sway Smart Contract, add the following to your Sway file:

```sway\nlibrary;

use std::alloc::alloc_bytes;

// ANCHOR: import
use sway_libs::bytecode::*;
// ANCHOR_END: import

// ANCHOR: known_issue
fn make_mutable(not_mutable_bytecode: Vec<u8>) {
    // Copy the bytecode to a newly allocated memory to avoid memory ownership error.
    let mut bytecode_slice = raw_slice::from_parts::<u8>(
        alloc_bytes(not_mutable_bytecode.len()),
        not_mutable_bytecode
            .len(),
    );
    not_mutable_bytecode
        .ptr()
        .copy_bytes_to(bytecode_slice.ptr(), not_mutable_bytecode.len());
    let mut bytecode_vec = Vec::from(bytecode_slice);
    // You may now use `bytecode_vec` in your computation and verification function calls
}
// ANCHOR_END: known_issue

// ANCHOR: swap_configurables
fn swap(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let resulting_bytecode: Vec<u8> = swap_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: swap_configurables

// ANCHOR: compute_bytecode_root
fn compute_bytecode(my_bytecode: Vec<u8>) {
    let root: BytecodeRoot = compute_bytecode_root(my_bytecode);
}

fn compute_bytecode_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let root: BytecodeRoot = compute_bytecode_root_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_bytecode_root

// ANCHOR: verify_contract_bytecode
fn verify_contract(my_contract: ContractId, my_bytecode: Vec<u8>) {
    verify_contract_bytecode(my_contract, my_bytecode);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

fn verify_contract_configurables(
    my_contract: ContractId,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_contract_bytecode_with_configurables(my_contract, my_bytecode, my_configurables);
    // By reaching this line the contract has been verified to match the bytecode provided.
}
// ANCHOR_END: verify_contract_bytecode

// ANCHOR: compute_predicate_address
fn compute_predicate(my_bytecode: Vec<u8>) {
    let address: Address = compute_predicate_address(my_bytecode);
}

fn compute_predicate_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let address: Address = compute_predicate_address_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_predicate_address

// ANCHOR: predicate_address_from_root
fn predicate_address(my_root: BytecodeRoot) {
    let address: Address = predicate_address_from_root(my_root);
}
// ANCHOR_END: predicate_address_from_root

// ANCHOR: verify_predicate_address
fn verify_predicate(my_predicate: Address, my_bytecode: Vec<u8>) {
    verify_predicate_address(my_predicate, my_bytecode);
    // By reaching this line the predicate bytecode matches the address provided.
}

fn verify_predicate_configurables(
    my_predicate: Address,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_predicate_address_with_configurables(my_predicate, my_bytecode, my_configurables);
    // By reaching this line the predicate bytecode matches the address provided.
}
// ANCHOR_END: verify_predicate_address\n```

Using the Bytecode Library In Sway

Once imported, using the Bytecode Library is as simple as calling the desired function. Here is a list of function definitions that you may use.

  • compute_bytecode_root()
  • compute_bytecode_root_with_configurables()
  • compute_predicate_address()
  • compute_predicate_address_with_configurables()
  • predicate_address_from_root()
  • swap_configurables()
  • verify_contract_bytecode()
  • verify_contract_bytecode_with_configurables()
  • verify_predicate_address()
  • verify_predicate_address_with_configurables()

Known Issues

Please note that if you are passing the bytecode from the SDK and are including configurable values, the Vec<u8> bytecode provided must be copied to be mutable. The following can be added to make your bytecode mutable:

```sway\nlibrary;

use std::alloc::alloc_bytes;

// ANCHOR: import
use sway_libs::bytecode::*;
// ANCHOR_END: import

// ANCHOR: known_issue
fn make_mutable(not_mutable_bytecode: Vec<u8>) {
    // Copy the bytecode to a newly allocated memory to avoid memory ownership error.
    let mut bytecode_slice = raw_slice::from_parts::<u8>(
        alloc_bytes(not_mutable_bytecode.len()),
        not_mutable_bytecode
            .len(),
    );
    not_mutable_bytecode
        .ptr()
        .copy_bytes_to(bytecode_slice.ptr(), not_mutable_bytecode.len());
    let mut bytecode_vec = Vec::from(bytecode_slice);
    // You may now use `bytecode_vec` in your computation and verification function calls
}
// ANCHOR_END: known_issue

// ANCHOR: swap_configurables
fn swap(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let resulting_bytecode: Vec<u8> = swap_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: swap_configurables

// ANCHOR: compute_bytecode_root
fn compute_bytecode(my_bytecode: Vec<u8>) {
    let root: BytecodeRoot = compute_bytecode_root(my_bytecode);
}

fn compute_bytecode_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let root: BytecodeRoot = compute_bytecode_root_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_bytecode_root

// ANCHOR: verify_contract_bytecode
fn verify_contract(my_contract: ContractId, my_bytecode: Vec<u8>) {
    verify_contract_bytecode(my_contract, my_bytecode);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

fn verify_contract_configurables(
    my_contract: ContractId,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_contract_bytecode_with_configurables(my_contract, my_bytecode, my_configurables);
    // By reaching this line the contract has been verified to match the bytecode provided.
}
// ANCHOR_END: verify_contract_bytecode

// ANCHOR: compute_predicate_address
fn compute_predicate(my_bytecode: Vec<u8>) {
    let address: Address = compute_predicate_address(my_bytecode);
}

fn compute_predicate_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let address: Address = compute_predicate_address_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_predicate_address

// ANCHOR: predicate_address_from_root
fn predicate_address(my_root: BytecodeRoot) {
    let address: Address = predicate_address_from_root(my_root);
}
// ANCHOR_END: predicate_address_from_root

// ANCHOR: verify_predicate_address
fn verify_predicate(my_predicate: Address, my_bytecode: Vec<u8>) {
    verify_predicate_address(my_predicate, my_bytecode);
    // By reaching this line the predicate bytecode matches the address provided.
}

fn verify_predicate_configurables(
    my_predicate: Address,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_predicate_address_with_configurables(my_predicate, my_bytecode, my_configurables);
    // By reaching this line the predicate bytecode matches the address provided.
}
// ANCHOR_END: verify_predicate_address\n```

Basic Functionality

The examples below are intended for internal contract calls. If you are passing bytecode from the SDK, please follow the steps listed above in known issues to avoid the memory ownership error.

Swapping Configurables

Given some bytecode, you may swap the configurables of both Contracts and Predicates by calling the swap_configurables() function.

```sway\nlibrary;

use std::alloc::alloc_bytes;

// ANCHOR: import
use sway_libs::bytecode::*;
// ANCHOR_END: import

// ANCHOR: known_issue
fn make_mutable(not_mutable_bytecode: Vec<u8>) {
    // Copy the bytecode to a newly allocated memory to avoid memory ownership error.
    let mut bytecode_slice = raw_slice::from_parts::<u8>(
        alloc_bytes(not_mutable_bytecode.len()),
        not_mutable_bytecode
            .len(),
    );
    not_mutable_bytecode
        .ptr()
        .copy_bytes_to(bytecode_slice.ptr(), not_mutable_bytecode.len());
    let mut bytecode_vec = Vec::from(bytecode_slice);
    // You may now use `bytecode_vec` in your computation and verification function calls
}
// ANCHOR_END: known_issue

// ANCHOR: swap_configurables
fn swap(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let resulting_bytecode: Vec<u8> = swap_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: swap_configurables

// ANCHOR: compute_bytecode_root
fn compute_bytecode(my_bytecode: Vec<u8>) {
    let root: BytecodeRoot = compute_bytecode_root(my_bytecode);
}

fn compute_bytecode_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let root: BytecodeRoot = compute_bytecode_root_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_bytecode_root

// ANCHOR: verify_contract_bytecode
fn verify_contract(my_contract: ContractId, my_bytecode: Vec<u8>) {
    verify_contract_bytecode(my_contract, my_bytecode);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

fn verify_contract_configurables(
    my_contract: ContractId,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_contract_bytecode_with_configurables(my_contract, my_bytecode, my_configurables);
    // By reaching this line the contract has been verified to match the bytecode provided.
}
// ANCHOR_END: verify_contract_bytecode

// ANCHOR: compute_predicate_address
fn compute_predicate(my_bytecode: Vec<u8>) {
    let address: Address = compute_predicate_address(my_bytecode);
}

fn compute_predicate_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let address: Address = compute_predicate_address_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_predicate_address

// ANCHOR: predicate_address_from_root
fn predicate_address(my_root: BytecodeRoot) {
    let address: Address = predicate_address_from_root(my_root);
}
// ANCHOR_END: predicate_address_from_root

// ANCHOR: verify_predicate_address
fn verify_predicate(my_predicate: Address, my_bytecode: Vec<u8>) {
    verify_predicate_address(my_predicate, my_bytecode);
    // By reaching this line the predicate bytecode matches the address provided.
}

fn verify_predicate_configurables(
    my_predicate: Address,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_predicate_address_with_configurables(my_predicate, my_bytecode, my_configurables);
    // By reaching this line the predicate bytecode matches the address provided.
}
// ANCHOR_END: verify_predicate_address\n```

Contracts

Computing the Bytecode Root

To compute a contract's bytecode root you may call the compute_bytecode_root() or compute_bytecode_root_with_configurables() functions.

```sway\nlibrary;

use std::alloc::alloc_bytes;

// ANCHOR: import
use sway_libs::bytecode::*;
// ANCHOR_END: import

// ANCHOR: known_issue
fn make_mutable(not_mutable_bytecode: Vec<u8>) {
    // Copy the bytecode to a newly allocated memory to avoid memory ownership error.
    let mut bytecode_slice = raw_slice::from_parts::<u8>(
        alloc_bytes(not_mutable_bytecode.len()),
        not_mutable_bytecode
            .len(),
    );
    not_mutable_bytecode
        .ptr()
        .copy_bytes_to(bytecode_slice.ptr(), not_mutable_bytecode.len());
    let mut bytecode_vec = Vec::from(bytecode_slice);
    // You may now use `bytecode_vec` in your computation and verification function calls
}
// ANCHOR_END: known_issue

// ANCHOR: swap_configurables
fn swap(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let resulting_bytecode: Vec<u8> = swap_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: swap_configurables

// ANCHOR: compute_bytecode_root
fn compute_bytecode(my_bytecode: Vec<u8>) {
    let root: BytecodeRoot = compute_bytecode_root(my_bytecode);
}

fn compute_bytecode_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let root: BytecodeRoot = compute_bytecode_root_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_bytecode_root

// ANCHOR: verify_contract_bytecode
fn verify_contract(my_contract: ContractId, my_bytecode: Vec<u8>) {
    verify_contract_bytecode(my_contract, my_bytecode);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

fn verify_contract_configurables(
    my_contract: ContractId,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_contract_bytecode_with_configurables(my_contract, my_bytecode, my_configurables);
    // By reaching this line the contract has been verified to match the bytecode provided.
}
// ANCHOR_END: verify_contract_bytecode

// ANCHOR: compute_predicate_address
fn compute_predicate(my_bytecode: Vec<u8>) {
    let address: Address = compute_predicate_address(my_bytecode);
}

fn compute_predicate_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let address: Address = compute_predicate_address_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_predicate_address

// ANCHOR: predicate_address_from_root
fn predicate_address(my_root: BytecodeRoot) {
    let address: Address = predicate_address_from_root(my_root);
}
// ANCHOR_END: predicate_address_from_root

// ANCHOR: verify_predicate_address
fn verify_predicate(my_predicate: Address, my_bytecode: Vec<u8>) {
    verify_predicate_address(my_predicate, my_bytecode);
    // By reaching this line the predicate bytecode matches the address provided.
}

fn verify_predicate_configurables(
    my_predicate: Address,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_predicate_address_with_configurables(my_predicate, my_bytecode, my_configurables);
    // By reaching this line the predicate bytecode matches the address provided.
}
// ANCHOR_END: verify_predicate_address\n```

Verifying a Contract's Bytecode Root

To verify a contract's bytecode root you may call verify_bytecode_root() or verify_contract_bytecode_with_configurables() functions.

```sway\nlibrary;

use std::alloc::alloc_bytes;

// ANCHOR: import
use sway_libs::bytecode::*;
// ANCHOR_END: import

// ANCHOR: known_issue
fn make_mutable(not_mutable_bytecode: Vec<u8>) {
    // Copy the bytecode to a newly allocated memory to avoid memory ownership error.
    let mut bytecode_slice = raw_slice::from_parts::<u8>(
        alloc_bytes(not_mutable_bytecode.len()),
        not_mutable_bytecode
            .len(),
    );
    not_mutable_bytecode
        .ptr()
        .copy_bytes_to(bytecode_slice.ptr(), not_mutable_bytecode.len());
    let mut bytecode_vec = Vec::from(bytecode_slice);
    // You may now use `bytecode_vec` in your computation and verification function calls
}
// ANCHOR_END: known_issue

// ANCHOR: swap_configurables
fn swap(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let resulting_bytecode: Vec<u8> = swap_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: swap_configurables

// ANCHOR: compute_bytecode_root
fn compute_bytecode(my_bytecode: Vec<u8>) {
    let root: BytecodeRoot = compute_bytecode_root(my_bytecode);
}

fn compute_bytecode_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let root: BytecodeRoot = compute_bytecode_root_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_bytecode_root

// ANCHOR: verify_contract_bytecode
fn verify_contract(my_contract: ContractId, my_bytecode: Vec<u8>) {
    verify_contract_bytecode(my_contract, my_bytecode);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

fn verify_contract_configurables(
    my_contract: ContractId,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_contract_bytecode_with_configurables(my_contract, my_bytecode, my_configurables);
    // By reaching this line the contract has been verified to match the bytecode provided.
}
// ANCHOR_END: verify_contract_bytecode

// ANCHOR: compute_predicate_address
fn compute_predicate(my_bytecode: Vec<u8>) {
    let address: Address = compute_predicate_address(my_bytecode);
}

fn compute_predicate_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let address: Address = compute_predicate_address_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_predicate_address

// ANCHOR: predicate_address_from_root
fn predicate_address(my_root: BytecodeRoot) {
    let address: Address = predicate_address_from_root(my_root);
}
// ANCHOR_END: predicate_address_from_root

// ANCHOR: verify_predicate_address
fn verify_predicate(my_predicate: Address, my_bytecode: Vec<u8>) {
    verify_predicate_address(my_predicate, my_bytecode);
    // By reaching this line the predicate bytecode matches the address provided.
}

fn verify_predicate_configurables(
    my_predicate: Address,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_predicate_address_with_configurables(my_predicate, my_bytecode, my_configurables);
    // By reaching this line the predicate bytecode matches the address provided.
}
// ANCHOR_END: verify_predicate_address\n```

Predicates

Computing the Address from Bytecode

To compute a predicate's address you may call the compute_predicate_address() or compute_predicate_address_with_configurables() functions.

```sway\nlibrary;

use std::alloc::alloc_bytes;

// ANCHOR: import
use sway_libs::bytecode::*;
// ANCHOR_END: import

// ANCHOR: known_issue
fn make_mutable(not_mutable_bytecode: Vec<u8>) {
    // Copy the bytecode to a newly allocated memory to avoid memory ownership error.
    let mut bytecode_slice = raw_slice::from_parts::<u8>(
        alloc_bytes(not_mutable_bytecode.len()),
        not_mutable_bytecode
            .len(),
    );
    not_mutable_bytecode
        .ptr()
        .copy_bytes_to(bytecode_slice.ptr(), not_mutable_bytecode.len());
    let mut bytecode_vec = Vec::from(bytecode_slice);
    // You may now use `bytecode_vec` in your computation and verification function calls
}
// ANCHOR_END: known_issue

// ANCHOR: swap_configurables
fn swap(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let resulting_bytecode: Vec<u8> = swap_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: swap_configurables

// ANCHOR: compute_bytecode_root
fn compute_bytecode(my_bytecode: Vec<u8>) {
    let root: BytecodeRoot = compute_bytecode_root(my_bytecode);
}

fn compute_bytecode_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let root: BytecodeRoot = compute_bytecode_root_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_bytecode_root

// ANCHOR: verify_contract_bytecode
fn verify_contract(my_contract: ContractId, my_bytecode: Vec<u8>) {
    verify_contract_bytecode(my_contract, my_bytecode);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

fn verify_contract_configurables(
    my_contract: ContractId,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_contract_bytecode_with_configurables(my_contract, my_bytecode, my_configurables);
    // By reaching this line the contract has been verified to match the bytecode provided.
}
// ANCHOR_END: verify_contract_bytecode

// ANCHOR: compute_predicate_address
fn compute_predicate(my_bytecode: Vec<u8>) {
    let address: Address = compute_predicate_address(my_bytecode);
}

fn compute_predicate_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let address: Address = compute_predicate_address_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_predicate_address

// ANCHOR: predicate_address_from_root
fn predicate_address(my_root: BytecodeRoot) {
    let address: Address = predicate_address_from_root(my_root);
}
// ANCHOR_END: predicate_address_from_root

// ANCHOR: verify_predicate_address
fn verify_predicate(my_predicate: Address, my_bytecode: Vec<u8>) {
    verify_predicate_address(my_predicate, my_bytecode);
    // By reaching this line the predicate bytecode matches the address provided.
}

fn verify_predicate_configurables(
    my_predicate: Address,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_predicate_address_with_configurables(my_predicate, my_bytecode, my_configurables);
    // By reaching this line the predicate bytecode matches the address provided.
}
// ANCHOR_END: verify_predicate_address\n```

Computing the Address from a Root

If you have the root of a predicate, you may compute it's corresponding predicate address by calling the predicate_address_from_root() function.

```sway\nlibrary;

use std::alloc::alloc_bytes;

// ANCHOR: import
use sway_libs::bytecode::*;
// ANCHOR_END: import

// ANCHOR: known_issue
fn make_mutable(not_mutable_bytecode: Vec<u8>) {
    // Copy the bytecode to a newly allocated memory to avoid memory ownership error.
    let mut bytecode_slice = raw_slice::from_parts::<u8>(
        alloc_bytes(not_mutable_bytecode.len()),
        not_mutable_bytecode
            .len(),
    );
    not_mutable_bytecode
        .ptr()
        .copy_bytes_to(bytecode_slice.ptr(), not_mutable_bytecode.len());
    let mut bytecode_vec = Vec::from(bytecode_slice);
    // You may now use `bytecode_vec` in your computation and verification function calls
}
// ANCHOR_END: known_issue

// ANCHOR: swap_configurables
fn swap(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let resulting_bytecode: Vec<u8> = swap_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: swap_configurables

// ANCHOR: compute_bytecode_root
fn compute_bytecode(my_bytecode: Vec<u8>) {
    let root: BytecodeRoot = compute_bytecode_root(my_bytecode);
}

fn compute_bytecode_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let root: BytecodeRoot = compute_bytecode_root_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_bytecode_root

// ANCHOR: verify_contract_bytecode
fn verify_contract(my_contract: ContractId, my_bytecode: Vec<u8>) {
    verify_contract_bytecode(my_contract, my_bytecode);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

fn verify_contract_configurables(
    my_contract: ContractId,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_contract_bytecode_with_configurables(my_contract, my_bytecode, my_configurables);
    // By reaching this line the contract has been verified to match the bytecode provided.
}
// ANCHOR_END: verify_contract_bytecode

// ANCHOR: compute_predicate_address
fn compute_predicate(my_bytecode: Vec<u8>) {
    let address: Address = compute_predicate_address(my_bytecode);
}

fn compute_predicate_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let address: Address = compute_predicate_address_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_predicate_address

// ANCHOR: predicate_address_from_root
fn predicate_address(my_root: BytecodeRoot) {
    let address: Address = predicate_address_from_root(my_root);
}
// ANCHOR_END: predicate_address_from_root

// ANCHOR: verify_predicate_address
fn verify_predicate(my_predicate: Address, my_bytecode: Vec<u8>) {
    verify_predicate_address(my_predicate, my_bytecode);
    // By reaching this line the predicate bytecode matches the address provided.
}

fn verify_predicate_configurables(
    my_predicate: Address,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_predicate_address_with_configurables(my_predicate, my_bytecode, my_configurables);
    // By reaching this line the predicate bytecode matches the address provided.
}
// ANCHOR_END: verify_predicate_address\n```

Verifying the Address

To verify a predicates's address you may call verify_predicate_address() or verify_predicate_address_with_configurables() functions.

```sway\nlibrary;

use std::alloc::alloc_bytes;

// ANCHOR: import
use sway_libs::bytecode::*;
// ANCHOR_END: import

// ANCHOR: known_issue
fn make_mutable(not_mutable_bytecode: Vec<u8>) {
    // Copy the bytecode to a newly allocated memory to avoid memory ownership error.
    let mut bytecode_slice = raw_slice::from_parts::<u8>(
        alloc_bytes(not_mutable_bytecode.len()),
        not_mutable_bytecode
            .len(),
    );
    not_mutable_bytecode
        .ptr()
        .copy_bytes_to(bytecode_slice.ptr(), not_mutable_bytecode.len());
    let mut bytecode_vec = Vec::from(bytecode_slice);
    // You may now use `bytecode_vec` in your computation and verification function calls
}
// ANCHOR_END: known_issue

// ANCHOR: swap_configurables
fn swap(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let resulting_bytecode: Vec<u8> = swap_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: swap_configurables

// ANCHOR: compute_bytecode_root
fn compute_bytecode(my_bytecode: Vec<u8>) {
    let root: BytecodeRoot = compute_bytecode_root(my_bytecode);
}

fn compute_bytecode_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let root: BytecodeRoot = compute_bytecode_root_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_bytecode_root

// ANCHOR: verify_contract_bytecode
fn verify_contract(my_contract: ContractId, my_bytecode: Vec<u8>) {
    verify_contract_bytecode(my_contract, my_bytecode);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

fn verify_contract_configurables(
    my_contract: ContractId,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_contract_bytecode_with_configurables(my_contract, my_bytecode, my_configurables);
    // By reaching this line the contract has been verified to match the bytecode provided.
}
// ANCHOR_END: verify_contract_bytecode

// ANCHOR: compute_predicate_address
fn compute_predicate(my_bytecode: Vec<u8>) {
    let address: Address = compute_predicate_address(my_bytecode);
}

fn compute_predicate_configurables(
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    let address: Address = compute_predicate_address_with_configurables(my_bytecode, my_configurables);
}
// ANCHOR_END: compute_predicate_address

// ANCHOR: predicate_address_from_root
fn predicate_address(my_root: BytecodeRoot) {
    let address: Address = predicate_address_from_root(my_root);
}
// ANCHOR_END: predicate_address_from_root

// ANCHOR: verify_predicate_address
fn verify_predicate(my_predicate: Address, my_bytecode: Vec<u8>) {
    verify_predicate_address(my_predicate, my_bytecode);
    // By reaching this line the predicate bytecode matches the address provided.
}

fn verify_predicate_configurables(
    my_predicate: Address,
    my_bytecode: Vec<u8>,
    my_configurables: ContractConfigurables,
) {
    let mut my_bytecode = my_bytecode;
    verify_predicate_address_with_configurables(my_predicate, my_bytecode, my_configurables);
    // By reaching this line the predicate bytecode matches the address provided.
}
// ANCHOR_END: verify_predicate_address\n```

Merkle Library

Merkle trees allow for on-chain verification of off-chain data. With the merkle root posted on-chain, the generation of proofs off-chain can provide verifiably true data.

For implementation details on the Merkle Library please see the Sway Libs Docs.

Importing the Merkle Library

In order to use the Merkle Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Merkle Library to your Sway Smart Contract, add the following to your Sway file:

```sway\ncontract;

// ANCHOR: import
use sway_libs::merkle::binary_proof::*;
// ANCHOR_END: import

abi MerkleExample {
    fn verify(
        merkle_root: b256,
        key: u64,
        leaf: b256,
        num_leaves: u64,
        proof: Vec<b256>,
    ) -> bool;
}

impl MerkleExample for Contract {
    fn verify(
        merkle_root: b256,
        key: u64,
        leaf: b256,
        num_leaves: u64,
        proof: Vec<b256>,
    ) -> bool {
        verify_proof(key, leaf, merkle_root, num_leaves, proof)
    }
}

// ANCHOR: leaf_digest
fn compute_leaf(hashed_data: b256) {
    let leaf: b256 = leaf_digest(hashed_data);
}
// ANCHOR_END: leaf_digest

// ANCHOR: node_digest
fn compute_node(leaf_a: b256, leaf_b: b256) {
    let node: b256 = node_digest(leaf_a, leaf_b);
}
// ANCHOR_END: node_digest

// ANCHOR: process_proof
fn process(key: u64, leaf: b256, num_leaves: u64, proof: Vec<b256>) {
    let merkle_root: b256 = process_proof(key, leaf, num_leaves, proof);
}
// ANCHOR_END: process_proof

// ANCHOR: verify_proof
fn verify(
    merkle_root: b256,
    key: u64,
    leaf: b256,
    num_leaves: u64,
    proof: Vec<b256>,
) {
    assert(verify_proof(key, leaf, merkle_root, num_leaves, proof));
}
// ANCHOR_END: verify_proof\n```

Using the Merkle Proof Library In Sway

Once imported, using the Merkle Proof library is as simple as calling the desired function. Here is a list of function definitions that you may use.

  • leaf_digest()
  • node_digest()
  • process_proof()
  • verify_proof()

Basic Functionality

Computing Leaves and Nodes

The Binary Proof currently allows for you to compute leaves and nodes of a merkle tree given the appropriate hash digest.

To compute a leaf use the leaf_digest() function:

```sway\ncontract;

// ANCHOR: import
use sway_libs::merkle::binary_proof::*;
// ANCHOR_END: import

abi MerkleExample {
    fn verify(
        merkle_root: b256,
        key: u64,
        leaf: b256,
        num_leaves: u64,
        proof: Vec<b256>,
    ) -> bool;
}

impl MerkleExample for Contract {
    fn verify(
        merkle_root: b256,
        key: u64,
        leaf: b256,
        num_leaves: u64,
        proof: Vec<b256>,
    ) -> bool {
        verify_proof(key, leaf, merkle_root, num_leaves, proof)
    }
}

// ANCHOR: leaf_digest
fn compute_leaf(hashed_data: b256) {
    let leaf: b256 = leaf_digest(hashed_data);
}
// ANCHOR_END: leaf_digest

// ANCHOR: node_digest
fn compute_node(leaf_a: b256, leaf_b: b256) {
    let node: b256 = node_digest(leaf_a, leaf_b);
}
// ANCHOR_END: node_digest

// ANCHOR: process_proof
fn process(key: u64, leaf: b256, num_leaves: u64, proof: Vec<b256>) {
    let merkle_root: b256 = process_proof(key, leaf, num_leaves, proof);
}
// ANCHOR_END: process_proof

// ANCHOR: verify_proof
fn verify(
    merkle_root: b256,
    key: u64,
    leaf: b256,
    num_leaves: u64,
    proof: Vec<b256>,
) {
    assert(verify_proof(key, leaf, merkle_root, num_leaves, proof));
}
// ANCHOR_END: verify_proof\n```

To compute a node given two leaves, use the node_digest() function:

```sway\ncontract;

// ANCHOR: import
use sway_libs::merkle::binary_proof::*;
// ANCHOR_END: import

abi MerkleExample {
    fn verify(
        merkle_root: b256,
        key: u64,
        leaf: b256,
        num_leaves: u64,
        proof: Vec<b256>,
    ) -> bool;
}

impl MerkleExample for Contract {
    fn verify(
        merkle_root: b256,
        key: u64,
        leaf: b256,
        num_leaves: u64,
        proof: Vec<b256>,
    ) -> bool {
        verify_proof(key, leaf, merkle_root, num_leaves, proof)
    }
}

// ANCHOR: leaf_digest
fn compute_leaf(hashed_data: b256) {
    let leaf: b256 = leaf_digest(hashed_data);
}
// ANCHOR_END: leaf_digest

// ANCHOR: node_digest
fn compute_node(leaf_a: b256, leaf_b: b256) {
    let node: b256 = node_digest(leaf_a, leaf_b);
}
// ANCHOR_END: node_digest

// ANCHOR: process_proof
fn process(key: u64, leaf: b256, num_leaves: u64, proof: Vec<b256>) {
    let merkle_root: b256 = process_proof(key, leaf, num_leaves, proof);
}
// ANCHOR_END: process_proof

// ANCHOR: verify_proof
fn verify(
    merkle_root: b256,
    key: u64,
    leaf: b256,
    num_leaves: u64,
    proof: Vec<b256>,
) {
    assert(verify_proof(key, leaf, merkle_root, num_leaves, proof));
}
// ANCHOR_END: verify_proof\n```

NOTE Order matters when computing a node.

Computing the Merkle Root

To compute a Merkle root given a proof, use the process_proof() function.

```sway\ncontract;

// ANCHOR: import
use sway_libs::merkle::binary_proof::*;
// ANCHOR_END: import

abi MerkleExample {
    fn verify(
        merkle_root: b256,
        key: u64,
        leaf: b256,
        num_leaves: u64,
        proof: Vec<b256>,
    ) -> bool;
}

impl MerkleExample for Contract {
    fn verify(
        merkle_root: b256,
        key: u64,
        leaf: b256,
        num_leaves: u64,
        proof: Vec<b256>,
    ) -> bool {
        verify_proof(key, leaf, merkle_root, num_leaves, proof)
    }
}

// ANCHOR: leaf_digest
fn compute_leaf(hashed_data: b256) {
    let leaf: b256 = leaf_digest(hashed_data);
}
// ANCHOR_END: leaf_digest

// ANCHOR: node_digest
fn compute_node(leaf_a: b256, leaf_b: b256) {
    let node: b256 = node_digest(leaf_a, leaf_b);
}
// ANCHOR_END: node_digest

// ANCHOR: process_proof
fn process(key: u64, leaf: b256, num_leaves: u64, proof: Vec<b256>) {
    let merkle_root: b256 = process_proof(key, leaf, num_leaves, proof);
}
// ANCHOR_END: process_proof

// ANCHOR: verify_proof
fn verify(
    merkle_root: b256,
    key: u64,
    leaf: b256,
    num_leaves: u64,
    proof: Vec<b256>,
) {
    assert(verify_proof(key, leaf, merkle_root, num_leaves, proof));
}
// ANCHOR_END: verify_proof\n```

Verifying a Proof

To verify a proof against a merkle root, use the verify_proof() function.

```sway\ncontract;

// ANCHOR: import
use sway_libs::merkle::binary_proof::*;
// ANCHOR_END: import

abi MerkleExample {
    fn verify(
        merkle_root: b256,
        key: u64,
        leaf: b256,
        num_leaves: u64,
        proof: Vec<b256>,
    ) -> bool;
}

impl MerkleExample for Contract {
    fn verify(
        merkle_root: b256,
        key: u64,
        leaf: b256,
        num_leaves: u64,
        proof: Vec<b256>,
    ) -> bool {
        verify_proof(key, leaf, merkle_root, num_leaves, proof)
    }
}

// ANCHOR: leaf_digest
fn compute_leaf(hashed_data: b256) {
    let leaf: b256 = leaf_digest(hashed_data);
}
// ANCHOR_END: leaf_digest

// ANCHOR: node_digest
fn compute_node(leaf_a: b256, leaf_b: b256) {
    let node: b256 = node_digest(leaf_a, leaf_b);
}
// ANCHOR_END: node_digest

// ANCHOR: process_proof
fn process(key: u64, leaf: b256, num_leaves: u64, proof: Vec<b256>) {
    let merkle_root: b256 = process_proof(key, leaf, num_leaves, proof);
}
// ANCHOR_END: process_proof

// ANCHOR: verify_proof
fn verify(
    merkle_root: b256,
    key: u64,
    leaf: b256,
    num_leaves: u64,
    proof: Vec<b256>,
) {
    assert(verify_proof(key, leaf, merkle_root, num_leaves, proof));
}
// ANCHOR_END: verify_proof\n```

Using the Merkle Proof Library with Fuels-rs

To generate a Merkle Tree and corresponding proof for your Sway Smart Contract, use the Fuel-Merkle crate.

Importing Into Your Project

The import the Fuel-Merkle crate, the following should be added to the project's Cargo.toml file under [dependencies]:

fuel-merkle = { version = "0.50.0" }

NOTE Make sure to use the latest version of the fuel-merkle crate.

Importing Into Your Rust File

The following should be added to your Rust file to use the Fuel-Merkle crate.

```rust\n// ANCHOR: import
use fuel_merkle::binary::in_memory::MerkleTree;
// ANCHOR_END: import
use fuels::{prelude::*, types::Bits256};
use sha2::{Digest, Sha256};

pub const LEAF: u8 = 0x00;

// Load abi from json
abigen!(Contract(
    name = "MerkleExample",
    abi = "merkle/out/release/merkle_examples-abi.json"
));

async fn get_contract_instance() -> (MerkleExample<WalletUnlocked>, WalletUnlocked) {
    // Launch a local network and deploy the contract
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(
            Some(1),             /* Single wallet */
            Some(1),             /* Single coin (UTXO) */
            Some(1_000_000_000), /* Amount per coin */
        ),
        None,
        None,
    )
    .await
    .unwrap();
    let wallet = wallets.pop().unwrap();

    let id = Contract::load_from(
        "./merkle/out/release/merkle_examples.bin",
        LoadConfiguration::default(),
    )
    .unwrap()
    .deploy(&wallet, TxPolicies::default())
    .await
    .unwrap();

    let instance = MerkleExample::new(id, wallet.clone());

    (instance, wallet)
}

#[tokio::test]
async fn rust_setup_example() {
    let (contract_instance, _id) = get_contract_instance().await;

    // ANCHOR: generating_a_tree
    // Create a new Merkle Tree and define leaves
    let mut tree = MerkleTree::new();
    let leaves = [b"A", b"B", b"C"].to_vec();

    // Hash the leaves and then push to the merkle tree
    for datum in &leaves {
        let mut hasher = Sha256::new();
        hasher.update(datum);
        let hash = hasher.finalize();
        tree.push(&hash);
    }
    // ANCHOR_END: generating_a_tree

    // ANCHOR: generating_proof
    // Define the key or index of the leaf you want to prove and the number of leaves
    let key: u64 = 0;

    // Get the merkle root and proof set
    let (merkle_root, proof_set) = tree.prove(key).unwrap();

    // Convert the proof set from Vec<Bytes32> to Vec<Bits256>
    let mut bits256_proof: Vec<Bits256> = Vec::new();
    for itterator in proof_set {
        bits256_proof.push(Bits256(itterator));
    }
    // ANCHOR_END: generating_proof

    // ANCHOR: verify_proof
    // Create the merkle leaf
    let mut leaf_hasher = Sha256::new();
    leaf_hasher.update(leaves[key as usize]);
    let hashed_leaf_data = leaf_hasher.finalize();
    let merkle_leaf = leaf_sum(&hashed_leaf_data);

    // Get the number of leaves or data points
    let num_leaves: u64 = leaves.len() as u64;

    // Call the Sway contract to verify the generated merkle proof
    let result: bool = contract_instance
        .methods()
        .verify(
            Bits256(merkle_root),
            key,
            Bits256(merkle_leaf),
            num_leaves,
            bits256_proof,
        )
        .call()
        .await
        .unwrap()
        .value;
    assert!(result);
    // ANCHOR_END: verify_proof
}

pub fn leaf_sum(data: &[u8]) -> [u8; 32] {
    let mut hash = Sha256::new();

    hash.update([LEAF]);
    hash.update(data);

    hash.finalize().into()
}\n```

Using Fuel-Merkle

Generating A Tree

To create a merkle tree using Fuel-Merkle is as simple as pushing your leaves in increasing order.

```rust\n// ANCHOR: import
use fuel_merkle::binary::in_memory::MerkleTree;
// ANCHOR_END: import
use fuels::{prelude::*, types::Bits256};
use sha2::{Digest, Sha256};

pub const LEAF: u8 = 0x00;

// Load abi from json
abigen!(Contract(
    name = "MerkleExample",
    abi = "merkle/out/release/merkle_examples-abi.json"
));

async fn get_contract_instance() -> (MerkleExample<WalletUnlocked>, WalletUnlocked) {
    // Launch a local network and deploy the contract
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(
            Some(1),             /* Single wallet */
            Some(1),             /* Single coin (UTXO) */
            Some(1_000_000_000), /* Amount per coin */
        ),
        None,
        None,
    )
    .await
    .unwrap();
    let wallet = wallets.pop().unwrap();

    let id = Contract::load_from(
        "./merkle/out/release/merkle_examples.bin",
        LoadConfiguration::default(),
    )
    .unwrap()
    .deploy(&wallet, TxPolicies::default())
    .await
    .unwrap();

    let instance = MerkleExample::new(id, wallet.clone());

    (instance, wallet)
}

#[tokio::test]
async fn rust_setup_example() {
    let (contract_instance, _id) = get_contract_instance().await;

    // ANCHOR: generating_a_tree
    // Create a new Merkle Tree and define leaves
    let mut tree = MerkleTree::new();
    let leaves = [b"A", b"B", b"C"].to_vec();

    // Hash the leaves and then push to the merkle tree
    for datum in &leaves {
        let mut hasher = Sha256::new();
        hasher.update(datum);
        let hash = hasher.finalize();
        tree.push(&hash);
    }
    // ANCHOR_END: generating_a_tree

    // ANCHOR: generating_proof
    // Define the key or index of the leaf you want to prove and the number of leaves
    let key: u64 = 0;

    // Get the merkle root and proof set
    let (merkle_root, proof_set) = tree.prove(key).unwrap();

    // Convert the proof set from Vec<Bytes32> to Vec<Bits256>
    let mut bits256_proof: Vec<Bits256> = Vec::new();
    for itterator in proof_set {
        bits256_proof.push(Bits256(itterator));
    }
    // ANCHOR_END: generating_proof

    // ANCHOR: verify_proof
    // Create the merkle leaf
    let mut leaf_hasher = Sha256::new();
    leaf_hasher.update(leaves[key as usize]);
    let hashed_leaf_data = leaf_hasher.finalize();
    let merkle_leaf = leaf_sum(&hashed_leaf_data);

    // Get the number of leaves or data points
    let num_leaves: u64 = leaves.len() as u64;

    // Call the Sway contract to verify the generated merkle proof
    let result: bool = contract_instance
        .methods()
        .verify(
            Bits256(merkle_root),
            key,
            Bits256(merkle_leaf),
            num_leaves,
            bits256_proof,
        )
        .call()
        .await
        .unwrap()
        .value;
    assert!(result);
    // ANCHOR_END: verify_proof
}

pub fn leaf_sum(data: &[u8]) -> [u8; 32] {
    let mut hash = Sha256::new();

    hash.update([LEAF]);
    hash.update(data);

    hash.finalize().into()
}\n```

Generating And Verifying A Proof

To generate a proof for a specific leaf, you must have the index or key of the leaf. Simply call the prove function:

```rust\n// ANCHOR: import
use fuel_merkle::binary::in_memory::MerkleTree;
// ANCHOR_END: import
use fuels::{prelude::*, types::Bits256};
use sha2::{Digest, Sha256};

pub const LEAF: u8 = 0x00;

// Load abi from json
abigen!(Contract(
    name = "MerkleExample",
    abi = "merkle/out/release/merkle_examples-abi.json"
));

async fn get_contract_instance() -> (MerkleExample<WalletUnlocked>, WalletUnlocked) {
    // Launch a local network and deploy the contract
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(
            Some(1),             /* Single wallet */
            Some(1),             /* Single coin (UTXO) */
            Some(1_000_000_000), /* Amount per coin */
        ),
        None,
        None,
    )
    .await
    .unwrap();
    let wallet = wallets.pop().unwrap();

    let id = Contract::load_from(
        "./merkle/out/release/merkle_examples.bin",
        LoadConfiguration::default(),
    )
    .unwrap()
    .deploy(&wallet, TxPolicies::default())
    .await
    .unwrap();

    let instance = MerkleExample::new(id, wallet.clone());

    (instance, wallet)
}

#[tokio::test]
async fn rust_setup_example() {
    let (contract_instance, _id) = get_contract_instance().await;

    // ANCHOR: generating_a_tree
    // Create a new Merkle Tree and define leaves
    let mut tree = MerkleTree::new();
    let leaves = [b"A", b"B", b"C"].to_vec();

    // Hash the leaves and then push to the merkle tree
    for datum in &leaves {
        let mut hasher = Sha256::new();
        hasher.update(datum);
        let hash = hasher.finalize();
        tree.push(&hash);
    }
    // ANCHOR_END: generating_a_tree

    // ANCHOR: generating_proof
    // Define the key or index of the leaf you want to prove and the number of leaves
    let key: u64 = 0;

    // Get the merkle root and proof set
    let (merkle_root, proof_set) = tree.prove(key).unwrap();

    // Convert the proof set from Vec<Bytes32> to Vec<Bits256>
    let mut bits256_proof: Vec<Bits256> = Vec::new();
    for itterator in proof_set {
        bits256_proof.push(Bits256(itterator));
    }
    // ANCHOR_END: generating_proof

    // ANCHOR: verify_proof
    // Create the merkle leaf
    let mut leaf_hasher = Sha256::new();
    leaf_hasher.update(leaves[key as usize]);
    let hashed_leaf_data = leaf_hasher.finalize();
    let merkle_leaf = leaf_sum(&hashed_leaf_data);

    // Get the number of leaves or data points
    let num_leaves: u64 = leaves.len() as u64;

    // Call the Sway contract to verify the generated merkle proof
    let result: bool = contract_instance
        .methods()
        .verify(
            Bits256(merkle_root),
            key,
            Bits256(merkle_leaf),
            num_leaves,
            bits256_proof,
        )
        .call()
        .await
        .unwrap()
        .value;
    assert!(result);
    // ANCHOR_END: verify_proof
}

pub fn leaf_sum(data: &[u8]) -> [u8; 32] {
    let mut hash = Sha256::new();

    hash.update([LEAF]);
    hash.update(data);

    hash.finalize().into()
}\n```

Once the proof has been generated, you may call the Sway Smart Contract's verify_proof function:

```rust\n// ANCHOR: import
use fuel_merkle::binary::in_memory::MerkleTree;
// ANCHOR_END: import
use fuels::{prelude::*, types::Bits256};
use sha2::{Digest, Sha256};

pub const LEAF: u8 = 0x00;

// Load abi from json
abigen!(Contract(
    name = "MerkleExample",
    abi = "merkle/out/release/merkle_examples-abi.json"
));

async fn get_contract_instance() -> (MerkleExample<WalletUnlocked>, WalletUnlocked) {
    // Launch a local network and deploy the contract
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(
            Some(1),             /* Single wallet */
            Some(1),             /* Single coin (UTXO) */
            Some(1_000_000_000), /* Amount per coin */
        ),
        None,
        None,
    )
    .await
    .unwrap();
    let wallet = wallets.pop().unwrap();

    let id = Contract::load_from(
        "./merkle/out/release/merkle_examples.bin",
        LoadConfiguration::default(),
    )
    .unwrap()
    .deploy(&wallet, TxPolicies::default())
    .await
    .unwrap();

    let instance = MerkleExample::new(id, wallet.clone());

    (instance, wallet)
}

#[tokio::test]
async fn rust_setup_example() {
    let (contract_instance, _id) = get_contract_instance().await;

    // ANCHOR: generating_a_tree
    // Create a new Merkle Tree and define leaves
    let mut tree = MerkleTree::new();
    let leaves = [b"A", b"B", b"C"].to_vec();

    // Hash the leaves and then push to the merkle tree
    for datum in &leaves {
        let mut hasher = Sha256::new();
        hasher.update(datum);
        let hash = hasher.finalize();
        tree.push(&hash);
    }
    // ANCHOR_END: generating_a_tree

    // ANCHOR: generating_proof
    // Define the key or index of the leaf you want to prove and the number of leaves
    let key: u64 = 0;

    // Get the merkle root and proof set
    let (merkle_root, proof_set) = tree.prove(key).unwrap();

    // Convert the proof set from Vec<Bytes32> to Vec<Bits256>
    let mut bits256_proof: Vec<Bits256> = Vec::new();
    for itterator in proof_set {
        bits256_proof.push(Bits256(itterator));
    }
    // ANCHOR_END: generating_proof

    // ANCHOR: verify_proof
    // Create the merkle leaf
    let mut leaf_hasher = Sha256::new();
    leaf_hasher.update(leaves[key as usize]);
    let hashed_leaf_data = leaf_hasher.finalize();
    let merkle_leaf = leaf_sum(&hashed_leaf_data);

    // Get the number of leaves or data points
    let num_leaves: u64 = leaves.len() as u64;

    // Call the Sway contract to verify the generated merkle proof
    let result: bool = contract_instance
        .methods()
        .verify(
            Bits256(merkle_root),
            key,
            Bits256(merkle_leaf),
            num_leaves,
            bits256_proof,
        )
        .call()
        .await
        .unwrap()
        .value;
    assert!(result);
    // ANCHOR_END: verify_proof
}

pub fn leaf_sum(data: &[u8]) -> [u8; 32] {
    let mut hash = Sha256::new();

    hash.update([LEAF]);
    hash.update(data);

    hash.finalize().into()
}\n```

Signed Integers Library

The Signed Integers library provides a library to use signed numbers in Sway. It has 6 distinct types: I8, I16, I32, I64, I128, I256. These types are stack allocated.

Internally the library uses the u8, u16, u32, u64, U128, u256 types to represent the underlying values of the signed integers.

For implementation details on the Signed Integers Library please see the Sway Libs Docs.

Importing the Signed Integer Library

In order to use the Signed Integer Number Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Signed Integer Number Library to your Sway Smart Contract, add the following to your Sway file:

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

In order to use any of the Signed Integer types, import them into your Sway project like so:

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

Basic Functionality

All the functionality is demonstrated with the I8 type, but all of the same functionality is available for the other types as well.

Instantiating a Signed Integer

Zero value

Once imported, a Signed Integer type can be instantiated defining a new variable and calling the new function.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

this newly initialized variable represents the value of 0.

The new function is functionally equivalent to the zero function.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

Positive and Negative Values

As the signed variants can only represent half as high a number as the unsigned variants (but with either a positive or negative sign), the try_from and neg_try_from functions will only work with half of the maximum value of the unsigned variant.

You can use the try_from function to create a new positive Signed Integer from a its unsigned variant.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

You can use the neg_try_from function to create a new negative Signed Integer from a its unsigned variant.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

With underlying value

As mentioned previously, the signed integers are internally represented by an unsigned integer, with its values divided into two halves, the bottom half of the values represent the negative values and the top half represent the positive values, and the middle value represents zero.

Therefore, for the lowest value representable by a i8, -128, the underlying value would be 0.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

For the zero value, the underlying value would be 128.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

And for the highest value representable by a i8, 127, the underlying value would be 255.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

Minimum and Maximum Values

To get the minimum and maximum values of a signed integer, use the min and max functions.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```
```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

Basic Mathematical Functions

Basic arithmetic operations are working as usual.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

Checking if a Signed Integer is Zero

The library also provides a helper function to easily check if a Signed Integer is zero.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::signed_integers::*;
// ANCHOR_END: import

// ANCHOR: import_8
use sway_libs::signed_integers::i8::I8;
// ANCHOR_END: import_8

fn initialize() {
    // ANCHOR: initialize
    let mut i8_value = I8::new();
    // ANCHOR_END: initialize

    // ANCHOR: zero
    let zero = I8::zero();
    // ANCHOR_END: zero

    // ANCHOR: zero_from_underlying
    let zero = I8::from_uint(128u8);
    // ANCHOR_END: zero_from_underlying

    // ANCHOR: neg_128_from_underlying
    let neg_128 = I8::from_uint(0u8);
    // ANCHOR_END: neg_128_from_underlying

    // ANCHOR: 127_from_underlying
    let pos_127 = I8::from_uint(255u8);
    // ANCHOR_END: 127_from_underlying

    // ANCHOR: min
    let min = I8::min();
    // ANCHOR_END: min

    // ANCHOR: max
    let max = I8::max();
    // ANCHOR_END: max
}

fn conversion() {
    // ANCHOR: positive_conversion
    let one = I8::try_from(1u8).unwrap();
    // ANCHOR_END: positive_conversion

    // ANCHOR: negative_conversion
    let negative_one = I8::neg_try_from(1u8).unwrap();
    // ANCHOR_END: negative_conversion
}

// ANCHOR: mathematical_ops
fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}
// ANCHOR_END: mathematical_ops

// ANCHOR: is_zero
fn is_zero() {
    let i8 = I8::zero();
    assert(i8.is_zero());
}
// ANCHOR_END: is_zero\n```

Known Issues

The current implementation of U128 will compile large bytecode sizes when performing mathematical computations. As a result, I128 and I256 inherit the same issue and could cause high transaction costs. This should be resolved with future optimizations of the Sway compiler.

Queue Library

A Queue is a linear structure which follows the First-In-First-Out (FIFO) principle. This means that the elements added first are the ones that get removed first.

For implementation details on the Queue Library please see the Sway Libs Docs.

Importing the Queue Library

In order to use the Queue Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Queue Library to your Sway Smart Contract, add the following to your Sway file:

```sway\nlibrary;

// ANCHOR: import
use sway_libs::queue::*;
// ANCHOR_END: import

fn instantiate() {
    // ANCHOR: instantiate
    let mut queue = Queue::new();
    // ANCHOR_END: instantiate
}

fn enqueue() {
    let mut queue = Queue::new();

    // ANCHOR: enqueue
    // Enqueue an element to the queue
    queue.enqueue(10u8);
    // ANCHOR_END: enqueue
}

fn dequeue() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: dequeue
    // Dequeue the first element and unwrap the value
    let first_item = queue.dequeue().unwrap();
    // ANCHOR_END: dequeue
}

fn peek() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: peek
    // Peek at the head of the queue
    let head_item = queue.peek();
    // ANCHOR_END: peek
}

fn length() {
    let mut queue = Queue::new();
    // ANCHOR: length
    // Checks if queue is empty (returns True or False)
    let is_queue_empty = queue.is_empty();

    // Returns length of queue
    let queue_length = queue.len();
    // ANCHOR_END: length
}\n```

Basic Functionality

Instantiating a New Queue

Once the Queue has been imported, you can create a new queue instance by calling the new function.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::queue::*;
// ANCHOR_END: import

fn instantiate() {
    // ANCHOR: instantiate
    let mut queue = Queue::new();
    // ANCHOR_END: instantiate
}

fn enqueue() {
    let mut queue = Queue::new();

    // ANCHOR: enqueue
    // Enqueue an element to the queue
    queue.enqueue(10u8);
    // ANCHOR_END: enqueue
}

fn dequeue() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: dequeue
    // Dequeue the first element and unwrap the value
    let first_item = queue.dequeue().unwrap();
    // ANCHOR_END: dequeue
}

fn peek() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: peek
    // Peek at the head of the queue
    let head_item = queue.peek();
    // ANCHOR_END: peek
}

fn length() {
    let mut queue = Queue::new();
    // ANCHOR: length
    // Checks if queue is empty (returns True or False)
    let is_queue_empty = queue.is_empty();

    // Returns length of queue
    let queue_length = queue.len();
    // ANCHOR_END: length
}\n```

Enqueuing elements

Adding elements to the Queue can be done using the enqueue function.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::queue::*;
// ANCHOR_END: import

fn instantiate() {
    // ANCHOR: instantiate
    let mut queue = Queue::new();
    // ANCHOR_END: instantiate
}

fn enqueue() {
    let mut queue = Queue::new();

    // ANCHOR: enqueue
    // Enqueue an element to the queue
    queue.enqueue(10u8);
    // ANCHOR_END: enqueue
}

fn dequeue() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: dequeue
    // Dequeue the first element and unwrap the value
    let first_item = queue.dequeue().unwrap();
    // ANCHOR_END: dequeue
}

fn peek() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: peek
    // Peek at the head of the queue
    let head_item = queue.peek();
    // ANCHOR_END: peek
}

fn length() {
    let mut queue = Queue::new();
    // ANCHOR: length
    // Checks if queue is empty (returns True or False)
    let is_queue_empty = queue.is_empty();

    // Returns length of queue
    let queue_length = queue.len();
    // ANCHOR_END: length
}\n```

Dequeuing Elements

To remove elements from the Queue, the dequeue function is used. This function follows the FIFO principle.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::queue::*;
// ANCHOR_END: import

fn instantiate() {
    // ANCHOR: instantiate
    let mut queue = Queue::new();
    // ANCHOR_END: instantiate
}

fn enqueue() {
    let mut queue = Queue::new();

    // ANCHOR: enqueue
    // Enqueue an element to the queue
    queue.enqueue(10u8);
    // ANCHOR_END: enqueue
}

fn dequeue() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: dequeue
    // Dequeue the first element and unwrap the value
    let first_item = queue.dequeue().unwrap();
    // ANCHOR_END: dequeue
}

fn peek() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: peek
    // Peek at the head of the queue
    let head_item = queue.peek();
    // ANCHOR_END: peek
}

fn length() {
    let mut queue = Queue::new();
    // ANCHOR: length
    // Checks if queue is empty (returns True or False)
    let is_queue_empty = queue.is_empty();

    // Returns length of queue
    let queue_length = queue.len();
    // ANCHOR_END: length
}\n```

Fetching the Head Element

To retrieve the element at the head of the Queue without removing it, you can use the peek function.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::queue::*;
// ANCHOR_END: import

fn instantiate() {
    // ANCHOR: instantiate
    let mut queue = Queue::new();
    // ANCHOR_END: instantiate
}

fn enqueue() {
    let mut queue = Queue::new();

    // ANCHOR: enqueue
    // Enqueue an element to the queue
    queue.enqueue(10u8);
    // ANCHOR_END: enqueue
}

fn dequeue() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: dequeue
    // Dequeue the first element and unwrap the value
    let first_item = queue.dequeue().unwrap();
    // ANCHOR_END: dequeue
}

fn peek() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: peek
    // Peek at the head of the queue
    let head_item = queue.peek();
    // ANCHOR_END: peek
}

fn length() {
    let mut queue = Queue::new();
    // ANCHOR: length
    // Checks if queue is empty (returns True or False)
    let is_queue_empty = queue.is_empty();

    // Returns length of queue
    let queue_length = queue.len();
    // ANCHOR_END: length
}\n```

Checking the Queue's Length

The is_empty and len functions can be used to check if the queue is empty and to get the number of elements in the queue respectively.

```sway\nlibrary;

// ANCHOR: import
use sway_libs::queue::*;
// ANCHOR_END: import

fn instantiate() {
    // ANCHOR: instantiate
    let mut queue = Queue::new();
    // ANCHOR_END: instantiate
}

fn enqueue() {
    let mut queue = Queue::new();

    // ANCHOR: enqueue
    // Enqueue an element to the queue
    queue.enqueue(10u8);
    // ANCHOR_END: enqueue
}

fn dequeue() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: dequeue
    // Dequeue the first element and unwrap the value
    let first_item = queue.dequeue().unwrap();
    // ANCHOR_END: dequeue
}

fn peek() {
    let mut queue = Queue::new();
    queue.enqueue(10u8);

    // ANCHOR: peek
    // Peek at the head of the queue
    let head_item = queue.peek();
    // ANCHOR_END: peek
}

fn length() {
    let mut queue = Queue::new();
    // ANCHOR: length
    // Checks if queue is empty (returns True or False)
    let is_queue_empty = queue.is_empty();

    // Returns length of queue
    let queue_length = queue.len();
    // ANCHOR_END: length
}\n```

Upgradability Library

The Upgradability Library provides functions that can be used to implement contract upgrades via simple upgradable proxies. The Upgradability Library implements the required and optional functionality from SRC-14 as well as additional functionality for ownership of the proxy contract.

For implementation details on the Upgradability Library please see the Sway Libs Docs.

Importing the Upgradability Library

In order to use the Upgradability library, Sway Libs and Sway Standards must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started. To add Sway Standards as a dependency please see the Sway Standards Book.

To import the Upgradability Library and SRC-14 Standard to your Sway Smart Contract, add the following to your Sway file:

```sway\ncontract;

// ANCHOR: import
use sway_libs::upgradability::*;
use standards::{src14::*, src5::*};
// ANCHOR_END: import

// ANCHOR: integrate_with_src14
use sway_libs::upgradability::{_proxy_owner, _proxy_target, _set_proxy_target};
use standards::{src14::{SRC14, SRC14Extension}, src5::State};

storage {
    SRC14 {
        /// The [ContractId] of the target contract.
        ///
        /// # Additional Information
        ///
        /// `target` is stored at sha256("storage_SRC14_0")
        target in 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55: Option<ContractId> = None,
        /// The [State] of the proxy owner.
        ///
        /// # Additional Information
        ///
        /// `proxy_owner` is stored at sha256("storage_SRC14_1")
        proxy_owner in 0xbb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754: State = State::Uninitialized,
    },
}

impl SRC14 for Contract {
    #[storage(read, write)]
    fn set_proxy_target(new_target: ContractId) {
        _set_proxy_target(new_target);
    }

    #[storage(read)]
    fn proxy_target() -> Option<ContractId> {
        _proxy_target()
    }
}

impl SRC14Extension for Contract {
    #[storage(read)]
    fn proxy_owner() -> State {
        _proxy_owner()
    }
}
// ANCHOR_END: integrate_with_src14

// ANCHOR: set_proxy_target
#[storage(read, write)]
fn set_proxy_target(new_target: ContractId) {
    _set_proxy_target(new_target);
}
// ANCHOR_END: set_proxy_target

// ANCHOR: proxy_target
#[storage(read)]
fn proxy_target() -> Option<ContractId> {
    _proxy_target()
}
// ANCHOR_END: proxy_target

// ANCHOR: set_proxy_owner
#[storage(write)]
fn set_proxy_owner(new_proxy_owner: State) {
    _set_proxy_owner(new_proxy_owner);
}
// ANCHOR_END: set_proxy_owner

// ANCHOR: proxy_owner
#[storage(read)]
fn proxy_owner() -> State {
    _proxy_owner()
}
// ANCHOR_END: proxy_owner

// ANCHOR: only_proxy_owner
#[storage(read)]
fn only_proxy_owner_may_call() {
    only_proxy_owner();
    // Only the proxy's owner may reach this line.
}
// ANCHOR_END: only_proxy_owner\n```

Integrating the Upgradability Library into the SRC-14 Standard

To implement the SRC-14 standard with the Upgradability library, be sure to add the Sway Standards dependency to your contract. The following demonstrates the integration of the Ownership library with the SRC-14 standard.

```sway\ncontract;

// ANCHOR: import
use sway_libs::upgradability::*;
use standards::{src14::*, src5::*};
// ANCHOR_END: import

// ANCHOR: integrate_with_src14
use sway_libs::upgradability::{_proxy_owner, _proxy_target, _set_proxy_target};
use standards::{src14::{SRC14, SRC14Extension}, src5::State};

storage {
    SRC14 {
        /// The [ContractId] of the target contract.
        ///
        /// # Additional Information
        ///
        /// `target` is stored at sha256("storage_SRC14_0")
        target in 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55: Option<ContractId> = None,
        /// The [State] of the proxy owner.
        ///
        /// # Additional Information
        ///
        /// `proxy_owner` is stored at sha256("storage_SRC14_1")
        proxy_owner in 0xbb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754: State = State::Uninitialized,
    },
}

impl SRC14 for Contract {
    #[storage(read, write)]
    fn set_proxy_target(new_target: ContractId) {
        _set_proxy_target(new_target);
    }

    #[storage(read)]
    fn proxy_target() -> Option<ContractId> {
        _proxy_target()
    }
}

impl SRC14Extension for Contract {
    #[storage(read)]
    fn proxy_owner() -> State {
        _proxy_owner()
    }
}
// ANCHOR_END: integrate_with_src14

// ANCHOR: set_proxy_target
#[storage(read, write)]
fn set_proxy_target(new_target: ContractId) {
    _set_proxy_target(new_target);
}
// ANCHOR_END: set_proxy_target

// ANCHOR: proxy_target
#[storage(read)]
fn proxy_target() -> Option<ContractId> {
    _proxy_target()
}
// ANCHOR_END: proxy_target

// ANCHOR: set_proxy_owner
#[storage(write)]
fn set_proxy_owner(new_proxy_owner: State) {
    _set_proxy_owner(new_proxy_owner);
}
// ANCHOR_END: set_proxy_owner

// ANCHOR: proxy_owner
#[storage(read)]
fn proxy_owner() -> State {
    _proxy_owner()
}
// ANCHOR_END: proxy_owner

// ANCHOR: only_proxy_owner
#[storage(read)]
fn only_proxy_owner_may_call() {
    only_proxy_owner();
    // Only the proxy's owner may reach this line.
}
// ANCHOR_END: only_proxy_owner\n```

NOTE An initialization method must be implemented to initialize the proxy target or proxy owner.

Basic Functionality

Setting and getting a Proxy Target

Once imported, the Upgradability Library's functions will be available. Use them to change the proxy target for your contract by calling the set_proxy_target() function.

```sway\ncontract;

// ANCHOR: import
use sway_libs::upgradability::*;
use standards::{src14::*, src5::*};
// ANCHOR_END: import

// ANCHOR: integrate_with_src14
use sway_libs::upgradability::{_proxy_owner, _proxy_target, _set_proxy_target};
use standards::{src14::{SRC14, SRC14Extension}, src5::State};

storage {
    SRC14 {
        /// The [ContractId] of the target contract.
        ///
        /// # Additional Information
        ///
        /// `target` is stored at sha256("storage_SRC14_0")
        target in 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55: Option<ContractId> = None,
        /// The [State] of the proxy owner.
        ///
        /// # Additional Information
        ///
        /// `proxy_owner` is stored at sha256("storage_SRC14_1")
        proxy_owner in 0xbb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754: State = State::Uninitialized,
    },
}

impl SRC14 for Contract {
    #[storage(read, write)]
    fn set_proxy_target(new_target: ContractId) {
        _set_proxy_target(new_target);
    }

    #[storage(read)]
    fn proxy_target() -> Option<ContractId> {
        _proxy_target()
    }
}

impl SRC14Extension for Contract {
    #[storage(read)]
    fn proxy_owner() -> State {
        _proxy_owner()
    }
}
// ANCHOR_END: integrate_with_src14

// ANCHOR: set_proxy_target
#[storage(read, write)]
fn set_proxy_target(new_target: ContractId) {
    _set_proxy_target(new_target);
}
// ANCHOR_END: set_proxy_target

// ANCHOR: proxy_target
#[storage(read)]
fn proxy_target() -> Option<ContractId> {
    _proxy_target()
}
// ANCHOR_END: proxy_target

// ANCHOR: set_proxy_owner
#[storage(write)]
fn set_proxy_owner(new_proxy_owner: State) {
    _set_proxy_owner(new_proxy_owner);
}
// ANCHOR_END: set_proxy_owner

// ANCHOR: proxy_owner
#[storage(read)]
fn proxy_owner() -> State {
    _proxy_owner()
}
// ANCHOR_END: proxy_owner

// ANCHOR: only_proxy_owner
#[storage(read)]
fn only_proxy_owner_may_call() {
    only_proxy_owner();
    // Only the proxy's owner may reach this line.
}
// ANCHOR_END: only_proxy_owner\n```

Use the proxy_target() method to get the current proxy target.

```sway\ncontract;

// ANCHOR: import
use sway_libs::upgradability::*;
use standards::{src14::*, src5::*};
// ANCHOR_END: import

// ANCHOR: integrate_with_src14
use sway_libs::upgradability::{_proxy_owner, _proxy_target, _set_proxy_target};
use standards::{src14::{SRC14, SRC14Extension}, src5::State};

storage {
    SRC14 {
        /// The [ContractId] of the target contract.
        ///
        /// # Additional Information
        ///
        /// `target` is stored at sha256("storage_SRC14_0")
        target in 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55: Option<ContractId> = None,
        /// The [State] of the proxy owner.
        ///
        /// # Additional Information
        ///
        /// `proxy_owner` is stored at sha256("storage_SRC14_1")
        proxy_owner in 0xbb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754: State = State::Uninitialized,
    },
}

impl SRC14 for Contract {
    #[storage(read, write)]
    fn set_proxy_target(new_target: ContractId) {
        _set_proxy_target(new_target);
    }

    #[storage(read)]
    fn proxy_target() -> Option<ContractId> {
        _proxy_target()
    }
}

impl SRC14Extension for Contract {
    #[storage(read)]
    fn proxy_owner() -> State {
        _proxy_owner()
    }
}
// ANCHOR_END: integrate_with_src14

// ANCHOR: set_proxy_target
#[storage(read, write)]
fn set_proxy_target(new_target: ContractId) {
    _set_proxy_target(new_target);
}
// ANCHOR_END: set_proxy_target

// ANCHOR: proxy_target
#[storage(read)]
fn proxy_target() -> Option<ContractId> {
    _proxy_target()
}
// ANCHOR_END: proxy_target

// ANCHOR: set_proxy_owner
#[storage(write)]
fn set_proxy_owner(new_proxy_owner: State) {
    _set_proxy_owner(new_proxy_owner);
}
// ANCHOR_END: set_proxy_owner

// ANCHOR: proxy_owner
#[storage(read)]
fn proxy_owner() -> State {
    _proxy_owner()
}
// ANCHOR_END: proxy_owner

// ANCHOR: only_proxy_owner
#[storage(read)]
fn only_proxy_owner_may_call() {
    only_proxy_owner();
    // Only the proxy's owner may reach this line.
}
// ANCHOR_END: only_proxy_owner\n```

Setting and getting a Proxy Owner

To change the proxy target for your contract use the set_proxy_owner() function.

```sway\ncontract;

// ANCHOR: import
use sway_libs::upgradability::*;
use standards::{src14::*, src5::*};
// ANCHOR_END: import

// ANCHOR: integrate_with_src14
use sway_libs::upgradability::{_proxy_owner, _proxy_target, _set_proxy_target};
use standards::{src14::{SRC14, SRC14Extension}, src5::State};

storage {
    SRC14 {
        /// The [ContractId] of the target contract.
        ///
        /// # Additional Information
        ///
        /// `target` is stored at sha256("storage_SRC14_0")
        target in 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55: Option<ContractId> = None,
        /// The [State] of the proxy owner.
        ///
        /// # Additional Information
        ///
        /// `proxy_owner` is stored at sha256("storage_SRC14_1")
        proxy_owner in 0xbb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754: State = State::Uninitialized,
    },
}

impl SRC14 for Contract {
    #[storage(read, write)]
    fn set_proxy_target(new_target: ContractId) {
        _set_proxy_target(new_target);
    }

    #[storage(read)]
    fn proxy_target() -> Option<ContractId> {
        _proxy_target()
    }
}

impl SRC14Extension for Contract {
    #[storage(read)]
    fn proxy_owner() -> State {
        _proxy_owner()
    }
}
// ANCHOR_END: integrate_with_src14

// ANCHOR: set_proxy_target
#[storage(read, write)]
fn set_proxy_target(new_target: ContractId) {
    _set_proxy_target(new_target);
}
// ANCHOR_END: set_proxy_target

// ANCHOR: proxy_target
#[storage(read)]
fn proxy_target() -> Option<ContractId> {
    _proxy_target()
}
// ANCHOR_END: proxy_target

// ANCHOR: set_proxy_owner
#[storage(write)]
fn set_proxy_owner(new_proxy_owner: State) {
    _set_proxy_owner(new_proxy_owner);
}
// ANCHOR_END: set_proxy_owner

// ANCHOR: proxy_owner
#[storage(read)]
fn proxy_owner() -> State {
    _proxy_owner()
}
// ANCHOR_END: proxy_owner

// ANCHOR: only_proxy_owner
#[storage(read)]
fn only_proxy_owner_may_call() {
    only_proxy_owner();
    // Only the proxy's owner may reach this line.
}
// ANCHOR_END: only_proxy_owner\n```

Use the proxy_owner() method to get the current proxy owner.

```sway\ncontract;

// ANCHOR: import
use sway_libs::upgradability::*;
use standards::{src14::*, src5::*};
// ANCHOR_END: import

// ANCHOR: integrate_with_src14
use sway_libs::upgradability::{_proxy_owner, _proxy_target, _set_proxy_target};
use standards::{src14::{SRC14, SRC14Extension}, src5::State};

storage {
    SRC14 {
        /// The [ContractId] of the target contract.
        ///
        /// # Additional Information
        ///
        /// `target` is stored at sha256("storage_SRC14_0")
        target in 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55: Option<ContractId> = None,
        /// The [State] of the proxy owner.
        ///
        /// # Additional Information
        ///
        /// `proxy_owner` is stored at sha256("storage_SRC14_1")
        proxy_owner in 0xbb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754: State = State::Uninitialized,
    },
}

impl SRC14 for Contract {
    #[storage(read, write)]
    fn set_proxy_target(new_target: ContractId) {
        _set_proxy_target(new_target);
    }

    #[storage(read)]
    fn proxy_target() -> Option<ContractId> {
        _proxy_target()
    }
}

impl SRC14Extension for Contract {
    #[storage(read)]
    fn proxy_owner() -> State {
        _proxy_owner()
    }
}
// ANCHOR_END: integrate_with_src14

// ANCHOR: set_proxy_target
#[storage(read, write)]
fn set_proxy_target(new_target: ContractId) {
    _set_proxy_target(new_target);
}
// ANCHOR_END: set_proxy_target

// ANCHOR: proxy_target
#[storage(read)]
fn proxy_target() -> Option<ContractId> {
    _proxy_target()
}
// ANCHOR_END: proxy_target

// ANCHOR: set_proxy_owner
#[storage(write)]
fn set_proxy_owner(new_proxy_owner: State) {
    _set_proxy_owner(new_proxy_owner);
}
// ANCHOR_END: set_proxy_owner

// ANCHOR: proxy_owner
#[storage(read)]
fn proxy_owner() -> State {
    _proxy_owner()
}
// ANCHOR_END: proxy_owner

// ANCHOR: only_proxy_owner
#[storage(read)]
fn only_proxy_owner_may_call() {
    only_proxy_owner();
    // Only the proxy's owner may reach this line.
}
// ANCHOR_END: only_proxy_owner\n```

Proxy access control

To restrict a function to only be callable by the proxy's owner, call the only_proxy_owner() function.

```sway\ncontract;

// ANCHOR: import
use sway_libs::upgradability::*;
use standards::{src14::*, src5::*};
// ANCHOR_END: import

// ANCHOR: integrate_with_src14
use sway_libs::upgradability::{_proxy_owner, _proxy_target, _set_proxy_target};
use standards::{src14::{SRC14, SRC14Extension}, src5::State};

storage {
    SRC14 {
        /// The [ContractId] of the target contract.
        ///
        /// # Additional Information
        ///
        /// `target` is stored at sha256("storage_SRC14_0")
        target in 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55: Option<ContractId> = None,
        /// The [State] of the proxy owner.
        ///
        /// # Additional Information
        ///
        /// `proxy_owner` is stored at sha256("storage_SRC14_1")
        proxy_owner in 0xbb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754: State = State::Uninitialized,
    },
}

impl SRC14 for Contract {
    #[storage(read, write)]
    fn set_proxy_target(new_target: ContractId) {
        _set_proxy_target(new_target);
    }

    #[storage(read)]
    fn proxy_target() -> Option<ContractId> {
        _proxy_target()
    }
}

impl SRC14Extension for Contract {
    #[storage(read)]
    fn proxy_owner() -> State {
        _proxy_owner()
    }
}
// ANCHOR_END: integrate_with_src14

// ANCHOR: set_proxy_target
#[storage(read, write)]
fn set_proxy_target(new_target: ContractId) {
    _set_proxy_target(new_target);
}
// ANCHOR_END: set_proxy_target

// ANCHOR: proxy_target
#[storage(read)]
fn proxy_target() -> Option<ContractId> {
    _proxy_target()
}
// ANCHOR_END: proxy_target

// ANCHOR: set_proxy_owner
#[storage(write)]
fn set_proxy_owner(new_proxy_owner: State) {
    _set_proxy_owner(new_proxy_owner);
}
// ANCHOR_END: set_proxy_owner

// ANCHOR: proxy_owner
#[storage(read)]
fn proxy_owner() -> State {
    _proxy_owner()
}
// ANCHOR_END: proxy_owner

// ANCHOR: only_proxy_owner
#[storage(read)]
fn only_proxy_owner_may_call() {
    only_proxy_owner();
    // Only the proxy's owner may reach this line.
}
// ANCHOR_END: only_proxy_owner\n```

SRC-2: Inline Documentation

The following standard intends to define the structure and organization of inline documentation for functions, structs, enums, storage, configurables, and more within the Sway Language. This is a living standard.

Motivation

The standard seeks to provide a better developer experience using Fuel's tooling and the Language Server. This will allow for better interoperability between applications and enable developers to quickly understand any external code they are using or implementing.

Prior Art

A number of pre-existing functions in the sway standard library, sway-applications, and sway-libs repositories have inline documentation. The inline documentation for these is already compatible with Fuel's VS Code extension. These however do not all follow the same structure and outline.

Specification

Functions

The following describes the structure and order of inline documentation for functions. Some sections MAY NOT apply to each function. When a section is not relevant it SHALL be omitted.

Functions: Description

This section has no header. A simple explanation of the function's intent or functionality. Example:

/// This function computes the hash of two numbers.

Functions: Additional Information

This section has a h1 header. This section is directly below the description and can provide additional information beyond the function's intent or functionality. Example:

/// # Additional Information
///
/// This function also has some complex behaviors.

Functions: Arguments

This section has a h1 header. Lists the arguments of the function's definition with the * symbol and describes each one. The list SHALL provide the name, type, and description. The argument SHALL be encapsulated between two backticks: argument. The type SHALL be encapsulated between two square brackets: [type]. Example:

/// # Arguments
///
/// * `argument_1`: [Identity] - This argument is a user to be hashed.

Functions: Returns

This section has a h1 header. Lists the return values of the function with the * symbol and describes each one. This list SHALL be in the order of the return index and provide the type and description. The type SHALL be encapsulated between two square brackets: [type]. Example:

/// # Returns
///
/// * [u64] - The number of hashes performed.

Functions: Reverts

This section has a h1 header. Lists the cases in which the function will revert starting with the * symbol. The list SHALL be in the order of occurrence within the function. Example:

/// # Reverts
///
/// * When `argument_1` or `argument_2` are a zero [b256].

Functions: Number of Storage Accesses

This section has a h1 header. Provides information on how many storage reads, writes, and clears occur within the function. Example:

/// # Number of Storage Accesses
///
/// * Reads: `1`
/// * Clears: `2`

Functions: Examples

This section has a h1 header. This section provides an example of the use of the function. This section is not required to follow the SRC-2 standard however encouraged for auxiliary and library functions. Example:

/// # Examples
///
/// ```sway
/// fn foo(argument_1: b256, argument_2: b256) {
///     let result = my_function(argument_1, argument_2);
/// }

Structs

The following describes the structure and order of inline documentation for structs. Some sections MAY NOT apply to each struct. When a section is not relevant it SHALL be omitted.

Structs: Description

This section has no header. A simple explanation of the struct's purpose or functionality. Example:

/// This struct contains information on an NFT.

Structs: Additional Information

This section has a h1 header. This section is directly below the description and can provide additional information beyond the struct's purpose or functionality. Example:

/// # Additional Information
///
/// This struct also has some complex behaviors.

Fields

The following describes the structure and order of inline documentation for fields within structs. Some sections MAY NOT apply to each field. When a section is not relevant it SHALL be omitted.

Fields: Description

This section has no header. Each field SHALL have its own description with a simple explanation of the field's purpose or functionality. Example:

/// This field represents an owner.
field_1: Identity,

Fields: Additional Information

This section has a h1 header. This section is directly below the description and can provide additional information beyond the field's purpose or functionality. Example:

/// # Additional Information
///
/// This field also has some complex behaviors.

Enums

The following describes the structure and order of inline documentation for enums. Some sections MAY NOT apply to each enum. When a section is not relevant it SHALL be omitted.

Enums: Description

This section has no header. A simple explanation of the enum's purpose or functionality. Example:

/// This enum holds the state of a contract.

Enums: Additional Information

This section has a h1 header. This section is directly below the description and can provide additional information beyond the enum's purpose or functionality. Example:

/// # Additional Information
///
/// This enum also has some complex behaviors.

Variant

The following describes the structure and order of inline documentation for fields within enums. Some sections MAY NOT apply to each field. When a section is not relevant it SHALL be omitted.

Variant: Description

This section has no header. Each variant SHALL have its own description with a simple explanation of the variant's purpose or functionality. Example:

/// This variant represents the uninitialized state of a contract.
variant_1: (),
/// This variant represents the initialized state of a contract.
variant_2: Identity,

Variant: Additional Information

This section has a h1 header. This section is directly below the description and can provide additional information beyond the variant's purpose or functionality. Example:

/// # Additional Information
///
/// This variant also has some complex behaviors.

Errors

In Sway, errors are recommended to be enums. They SHALL follow the same structure and order for inline documentation as described above for enums. Some sections MAY NOT apply to each error. When a section is not relevant it SHALL be omitted.

Logs

In Sway, logs are recommended to be structs. They SHALL follow the same structure and order for inline documentation as described above for structs. Some sections MAY NOT apply to each log. When a section is not relevant it SHALL be omitted.

Storage

The following describes the structure and order of inline documentation for variables within the storage block. Some sections MAY NOT apply to each storage variable. When a section is not relevant it SHALL be omitted.

Storage: Description

This section has no header. A simple explanation of the storage variable's purpose or functionality. Example:

/// This storage variable is used for state.

Storage: Additional Information

This section has a h1 header. This section is directly below the description and can provide additional information beyond the storage variable's purpose or functionality. Example:

/// # Additional Information
///
/// This storage variable maps a user to a state.

Configurable

The following describes the structure and order of inline documentation for variables in the configurable block. Some sections MAY NOT apply to each storage variable. When a section is not relevant it SHALL be omitted.

Configurable: Description

This section has no header. A simple explanation of the configurable variable's purpose or functionality. Example:

/// This configurable variable is used for an address.

Configurable: Additional Information

This section has a h1 header. This section is directly below the description and can provide additional information beyond the configurable variable's purpose or functionality. Example:

/// # Additional Information
///
/// This configurable variable makes security assumptions.

Other Sections

If the above described sections are not relevant for the information that needs to documented, a custom section with a arbitrary h1 header may be utilized.

Example:

/// # Recommended Message Style
///
/// We recommend that `expect` messages are used to describe the reason you *expect* the `Option` should be `Some`.

Rationale

The SRC-2 standard should help provide developers with an easy way to both quickly write inline documentation and get up to speed on other developers' code. This standard in combination with Fuel's VS Code extension provides readily accessible information on functions, structs, and enums

Screenshot 2023-05-10 125656

Backwards Compatibility

There are no standards that the SRC-2 standard requires to be backward compatible with.

Security Considerations

This standard will improve security by providing developers with relevant information such as revert cases.

Examples

Function Example

/// Ensures that the sender is the owner.
///
/// # Arguments
///
/// * `number`: [u64] - A value that is checked to be 5.
///
/// # Returns
///
/// * [bool] - Determines whether `number` is or is not 5.
///
/// # Reverts
///
/// * When the sender is not the owner.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
///
/// # Examples
///
/// ```sway
/// use ownable::Ownership;
///
/// storage {
///     owner: Ownership = Ownership::initialized(Identity::Address(Address::zero())),
/// }
///
/// fn foo() {
///     storage.owner.only_owner();
///     // Do stuff here
/// }
#[storage(read)]
pub fn only_owner(self, number: u64) -> bool {
    require(self.owner() == State::Initialized(msg_sender().unwrap()), AccessError::NotOwner);
    number == 5
}

Struct Examples

/// Metadata that is tied to an asset.
pub struct NFTMetadata {
    /// Represents the ID of this NFT.
    value: u64,
}
/// Log of a bid.
pub struct Bid {
    /// The number of coins that were bid.
    amount: u64,
    /// The user which placed this bid.
    bidder: Identity,
}

Enum Examples

/// Determines the state of ownership.
pub enum State {
    /// The ownership has not been set.
    Uninitialized: (),
    /// The user who has been given ownership.
    Initialized: Identity,
    /// The ownership has been given up and can never be set again.
    Revoked: (),
}
/// Error log for when access is denied.
pub enum AccessError {
    /// Emitted when the caller is not the owner of the contract.
    NotOwner: (),
}

Storage Examples

storage {
    /// An asset which is to be distributed.
    asset: Option<AssetId> = Option::None,
    /// Stores the ClaimState of users that have interacted with the Airdrop Distributor contract.
    ///
    /// # Additional Information
    ///
    /// Maps (user => claim)
    claims: StorageMap<Identity, ClaimState> = StorageMap {},
}

Configurable Example

configurable {
    /// The threshold required for activation.
    THRESHOLD: u64 = 5,
}

SRC-3: Minting and Burning Native Assets

The following standard enables the minting and burning of native assets for any fungible assets within the Sway Language. It seeks to define mint and burn functions defined separately from the SRC-20 standard.

Motivation

The intent of this standard is to separate the extensions of minting and burning from the SRC-20 standard.

Prior Art

Minting and burning were initially added to the SRC-20 standard.

Specification

Required Public Functions

The following functions MUST be implemented to follow the SRC-3 standard:

fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64)

This function MUST mint amount coins with a sub-identifier and transfer them to the recipient. This function MUST use the sub_id as the sub-identifier IF sub_id is Some, otherwise this function MUST assign a SubId if the sub_id argument is None. This function MAY contain arbitrary conditions for minting, and revert if those conditions are not met.

Mint Arguments
  • recipient - The Identity to which the newly minted asset is transferred to.
  • sub_id - The sub-identifier of the asset to mint. If this is None, a SubId MUST be assigned.
  • amount - The quantity of coins to mint.

fn burn(sub_id: SubId, amount: u64)

This function MUST burn amount coins with the sub-identifier sub_id and MUST ensure the AssetId of the asset is the sha-256 hash of (ContractId, SubId) for the implementing contract. This function MUST ensure at least amount coins have been transferred to the implementing contract. This function MUST update the total supply defined in the SRC-20 standard. This function MAY contain arbitrary conditions for burning, and revert if those conditions are not met.

Burn Arguments
  • sub_id - The sub-identifier of the asset to burn.
  • amount - The quantity of coins to burn.

Rationale

This standard has been added to enable compatibility between applications and allow minting and burning native assets per use case. This standard has been separated from the SRC-20 standard to allow for the minting and burning for all fungible assets, irrelevant of whether they are Native Assets or not.

Backwards Compatibility

This standard is compatible with Fuel's Native Assets ensuring its compatibility with the SRC-20 standard.

Security Considerations

This standard may introduce security considerations if no checks are implemented to ensure the calling of the mint() function is deemed valid or permitted. Checks are highly encouraged. The burn function may also introduce a security consideration if the total supply within the SRC-20 standard is not modified.

Example ABI

abi SRC3 {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64);
    #[payable]
    #[storage(read, write)]
    fn burn(sub_id: SubId, amount: u64);
}

Example Implementation

Single Native Asset

Example of the SRC-3 implementation where a contract only mints a single asset with one SubId.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src3-mint-burn/single_asset/src/single_asset.sw' -->

Multi Native Asset

Example of the SRC-3 implementation where a contract mints multiple assets with differing SubId values.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src3-mint-burn/multi_asset/src/multi_asset.sw' -->

SRC-5: Ownership

The following standard intends to enable the use of administrators or owners in Sway contracts.

Motivation

The standard seeks to provide a method for restricting access to particular users within a Sway contract.

Prior Art

The sway-libs repository contains a pre-existing Ownership library.

Ownership libraries exist for other ecosystems such as OpenZeppelin's Ownership library.

Specification

State

There SHALL be 3 states for any library implementing an ownership module in the following order:

Uninitialized

The Uninitialized state SHALL be set as the initial state if no owner or admin is set. The Uninitialized state MUST be used when an owner or admin MAY be set in the future.

Initialized

The Initialized state SHALL be set as the state if an owner or admin is set with an associated Identity type.

Revoked

The Revoked state SHALL be set when there is no owner or admin and there SHALL NOT be one set in the future.

Example:

pub enum State {
    Uninitialized: (),
    Initialized: Identity,
    Revoked: (),
}

Functions

The following functions MUST be implemented to follow the SRC-5 standard:

fn owner() -> State

This function SHALL return the current state of ownership for the contract where State is either Uninitialized, Initialized, or Revoked.

Errors

There SHALL be error handling.

NotOwner

This error MUST be emitted when only_owner() reverts.

Rationale

In order to provide a universal method of administrative capabilities, SRC-5 will further enable interoperability between applications and provide safeguards for smart contract security.

Backwards Compatibility

The SRC-5 standard is compatible with the sway-libs repository pre-existing Ownership library. Considerations should be made to best handle multiple owners or admins.

There are no standards that SRC-5 requires to be compatible with.

Security Considerations

The SRC-5 standard should help improve the security of Sway contracts and their interoperability.

Example ABI

abi SRC5 {
    #[storage(read)]
    fn owner() -> State;
}

Example Implementation

Uninitialized

Example of the SRC-5 implementation where a contract does not have an owner set at compile time with the intent to set it during runtime.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src5-ownership/uninitialized_example/src/uninitialized_example.sw' -->

Initialized

Example of the SRC-5 implementation where a contract has an owner set at compile time.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src5-ownership/initialized_example/src/initialized_example.sw' -->

SRC-6: Vault

The following standard allows for the implementation of a standard API for asset vaults such as yield-bearing asset vaults or asset wrappers. This standard is an optional add-on to the SRC-20 standard.

Motivation

Asset vaults allow users to own shares of variable amounts of assets, such as lending protocols which may have growing assets due to profits from interest. This pattern is highly useful and would greatly benefit from standardization.

Prior Art

Asset vaults have been thoroughly explored on Ethereum and with EIP 4626 they have their own standard for it. However as Fuel's Native Assets are fundamentally different from Ethereum's ERC-20 tokens, the implementation will differ, but the interface may be used as a reference.

Specification

Required public functions

The following functions MUST be implemented to follow the SRC-6 standard. Any contract that implements the SRC-6 standard MUST implement the SRC-20 standard.

fn deposit(receiver: Identity, vault_sub_id: SubId) -> u64

This function takes the receiver Identity and the SubId vault_sub_id of the sub-vault as an argument and returns the amount of shares minted to the receiver.

  • This function MUST allow for depositing of the underlying asset in exchange for pro-rata shares of the vault.
  • This function MAY reject arbitrary assets based on implementation and MUST revert if unaccepted assets are forwarded.
  • This function MAY reject any arbitrary receiver based on implementation and MUST revert in the case of a blacklisted or non-whitelisted receiver.
  • This function MUST mint an asset representing the pro-rata share of the vault, with the SubId of the sha256((underlying_asset, vault_sub_id)) digest, where underlying_asset is the AssetId of the deposited asset and the vault_sub_id is the id of the vault.
  • This function MUST emit a Deposit log.
  • This function MUST return the amount of minted shares.

fn withdraw(receiver: Identity, underlying_asset: AssetId, vault_sub_id: SubId) -> u64

This function takes the receiver Identity, the underlying_asset AssetId, and the vault_sub_id of the sub vault, as arguments and returns the amount of assets transferred to the receiver.

  • This function MUST allow for redeeming of the vault shares in exchange for a pro-rata amount of the underlying assets.
  • This function MUST revert if any AssetId other than the AssetId representing the underlying asset's shares for the given sub vault at vault_sub_id is forwarded. (i.e. transferred share's AssetId must be equal to AssetId::new(ContractId::this(), sha256((underlying_asset, vault_sub_id)))
  • This function MUST burn the received shares.
  • This function MUST emit a Withdraw log.
  • This function MUST return amount of assets transferred to the receiver.

fn managed_assets(underlying_asset: AssetId, vault_sub_id: SubId) -> u64

This function returns the total assets under management by vault. Includes assets controlled by the vault but not directly possessed by vault. It takes the underlying_asset AssetId and the vault_sub_id of the sub vault as arguments and returns the total amount of assets of AssetId under management by vault.

  • This function MUST return total amount of assets of underlying_asset AssetId under management by vault.
  • This function MUST return 0 if there are no assets of underlying_asset AssetId under management by vault.
  • This function MUST NOT revert under any circumstances.

fn max_depositable(receiver: Identity, underlying_asset: AssetId, vault_sub_id: SubId) -> Option<u64>

This is a helper function for getting the maximum amount of assets that can be deposited. It takes the hypothetical receiver Identity, the underlying_asset AssetId, and the vault_sub_id SubId of the sub vault as an arguments and returns the maximum amount of assets that can be deposited into the contract, for the given asset.

  • This function MUST return the maximum amount of assets that can be deposited into the contract, for the given underlying_asset, if the given vault_sub_id vault exists.
  • This function MUST return an Some(amount) if the given vault_sub_id vault exists.
  • This function MUST return an None if the given vault_sub_id vault does not exist.
  • This function MUST account for both global and user specific limits. For example: if deposits are disabled, even temporarily, MUST return 0.

fn max_withdrawable(receiver: Identity, underlying_asset: AssetId, vault_sub_id: SubId) -> Option<u64>

This is a helper function for getting maximum withdrawable. It takes the hypothetical receiver Identity, the underlying_asset AssetId, and the vault_sub_id SubId of the sub vault as an argument and returns the maximum amount of assets that can be withdrawn from the contract, for the given asset.

  • This function MUST return the maximum amount of assets that can be withdrawn from the contract, for the given underlying_asset, if the given vault_sub_id vault exists.
  • This function MUST return an Some(amount) if the given vault_sub_id vault exists.
  • This function MUST return an None if the given vault_sub_id vault does not exist.
  • This function MUST account for global limits. For example: if withdrawals are disabled, even temporarily, MUST return 0.

Required logs

The following logs MUST be emitted at the specified occasions.

Deposit

caller has called the deposit() method sending deposited_amount assets of the underlying_asset Asset to the subvault of vault_sub_id, in exchange for minted_shares shares sent to the receiver receiver.

The Deposit struct MUST be logged whenever new shares are minted via the deposit() method.

The Deposit log SHALL have the following fields.

caller: Identity

The caller field MUST represent the Identity which called the deposit function.

receiver: Identity

The receiver field MUST represent the Identity which received the vault shares.

underlying_asset: AssetId

The underlying_asset field MUST represent the AssetId of the asset which was deposited into the vault.

vault_sub_id: SubId

The vault_sub_id field MUST represent the SubId of the vault which was deposited into.

deposited_amount: u64

The deposited_amount field MUST represent the u64 amount of assets deposited into the vault.

minted_shares: u64

The minted_shares field MUST represent the u64 amount of shares minted.

Withdraw

caller has called the withdraw() method sending burned_shares shares in exchange for withdrawn_amount assets of the underlying_asset Asset from the subvault of vault_sub_id to the receiver receiver.

The Withdraw struct MUST be logged whenever shares are redeemed for assets via the withdraw() method.

The Withdraw log SHALL have the following fields.

caller: Identity

The caller field MUST represent the Identity which called the withdraw function.

receiver: Identity

The receiver field MUST represent the Identity which received the withdrawn assets.

underlying_asset: AssetId

The underlying_asset field MUST represent the AssetId of the asset that was withdrawn.

vault_sub_id: SubId

The vault_sub_id field MUST represent the SubId of the vault from which was withdrawn.

withdrawn_amount: u64

The withdrawn_amount field MUST represent the u64 amount of coins withdrawn.

burned_shares: u64

The burned_shares field MUST represent the u64 amount of shares burned.

Rationale

The ABI discussed covers the known use cases of asset vaults while allowing safe implementations.

Backwards Compatibility

This standard is fully compatible with the SRC-20 standard.

Security Considerations

Incorrect implementation of asset vaults could allow attackers to steal underlying assets. It is recommended to properly audit any code using this standard to ensure exploits are not possible.

Example ABI

abi SRC6 {
    #[payable]
    #[storage(read, write)]
    fn deposit(receiver: Identity, vault_sub_id: SubId) -> u64;

    #[payable]
    #[storage(read, write)]
    fn withdraw(receiver: Identity, underlying_asset: AssetId, vault_sub_id: SubId) -> u64;

    #[storage(read)]
    fn managed_assets(underlying_asset: AssetId, vault_sub_id: SubId) -> u64;

    #[storage(read)]
    fn max_depositable(receiver: Identity, underlying_asset: AssetId, vault_sub_id: SubId) -> Option<u64>;

    #[storage(read)]
    fn max_withdrawable(underlying_asset: AssetId, vault_sub_id: SubId) -> Option<u64>;
}

Example Implementation

Multi Asset Vault

A basic implementation of the vault standard that supports any number of sub vaults being created for every AssetId.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src6-vault/multi_asset_vault/src/main.sw' -->

Single Asset Vault

A basic implementation of the vault standard demonstrating how to restrict deposits and withdrawals to a single AssetId.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src6-vault/single_asset_vault/src/main.sw' -->

Single Asset Single Sub Vault

A basic implementation of the vault standard demonstrating how to restrict deposits and withdrawals to a single AssetId, and to a single Sub vault.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src6-vault/single_asset_single_sub_vault/src/main.sw' -->

SRC-7: Onchain Native Asset Metadata

The following standard attempts to define the retrieval of on-chain arbitrary metadata for any Native Asset. This standard should be used if a stateful approach is needed. Any contract that implements the SRC-7 standard MUST implement the SRC-20 standard.

Motivation

The SRC-7 standard seeks to enable stateful data-rich assets on the Fuel Network while maintaining compatibility between multiple assets minted by the same contract. The standard ensures type safety with the use of an enum and an Option. All metadata queries are done through a single function to facilitate cross-contract calls.

Prior Art

The use of generic metadata was originally found in the Sway-Lib's NFT Library which did not use Fuel's Native Assets. This library has since been deprecated.

A previous definition for a metadata standard was written in the original edit of the now defunct SRC-721. This has since been replaced with the SRC-20 standard as SubId was introduced to enable multiple assets to be minted from a single contract.

The standard takes inspiration from ENS's public resolver with the use of a String as the key. This should enable human-readable keys to help minimize errors and enable the standardization of certain keys, such as "image" as opposed to an enum or u64 representation of keys.

We also take a look at existing common metadata practices such as OpenSea's Metadata Standards and seek to stay backwards compatible with them while enabling more functionality. Through the combination of String keys and various return types, both pre-defined URIs or specific attributes may be stored and retrieved with the SRC-7 standard.

Specification

Metadata Type

The following describes an enum that wraps various metadata types into a single return type. There SHALL be the following variants in the Metadata enum:

B256

The B256 variant SHALL be used when the stored metadata for the corresponding AssetId and Sting key pair is of the b256 type.

Bytes

The Bytes variant SHALL be used when the stored metadata for the corresponding AssetId and String key pair is of the Bytes type. The Bytes variant should be used when storing custom data such as but not limited to structs and enums.

Int

The Int variant SHALL be used when the stored metadata for the corresponding AssetId and Sting key pair is of the u64 type.

String

The String variant SHALL be used when the stored metadata for the corresponding AssetId and String key pair is of the String type. The String variant MUST be used when a URI is required but MAY contain any arbitrary String data.

Required Functions

fn metadata(asset: AssetId, key: String) -> Option<Metadata>

This function MUST return valid metadata for the corresponding asset and key, where the data is either a B256, Bytes, Int, or String variant. If the asset does not exist or no metadata exists, the function MUST return None.

Logging

The following logs MUST be implemented and emitted to follow the SRC-7 standard.

  • IF a value is updated via a function call, a log MUST be emitted.
  • IF a value is embedded in a contract as a constant, configurable, or other manner, an event MUST be emitted at least once.

SetMetadataEvent

The SetMetadataEvent MUST be emitted when the metadata of an asset has updated.

There SHALL be the following fields in the SetMetadataEvent struct:

  • asset: The asset field SHALL be used for the corresponding AssetId for the asset that has been updated.
  • metadata: The metadata field SHALL be used for the corresponding Option<Metadata> which represents the metadata of the asset.
  • key: The key field SHALL be used for the corresponding String which represents the key used for storing the metadata.
  • sender: The sender field SHALL be used for the corresponding Identity which made the function call that has updated the metadata of the asset.

Example:

pub struct SetMetadataEvent {
    pub asset: AssetId,
    pub metadata: Option<Metadata>,
    pub key: String,
    pub sender: Identity,
}

Rationale

The SRC-7 standard should allow for stateful data-rich assets to interact with one another in a safe manner.

Backwards Compatibility

This standard is compatible with Fuel's Native Assets and the SRC-20 standard. It also maintains compatibility with existing standards in other ecosystems.

Security Considerations

This standard does not introduce any security concerns, as it does not call external contracts, nor does it define any mutations of the contract state.

Example ABI

abi SRC7 {
     #[storage(read)]
     fn metadata(asset: AssetId, key: String) -> Option<Metadata>;
}

Example Implementation

Single Native Asset

Example of the SRC-7 implementation where metadata exists for only a single asset with one SubId.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src7-metadata/single_asset/src/single_asset.sw' -->

Multi Native Asset

Example of the SRC-7 implementation where metadata exists for multiple assets with differing SubId values.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src7-metadata/multi_asset/src/multi_asset.sw' -->

SRC-8: Bridged Asset

The following standard attempts to define the retrieval of relevant on-chain metadata for any bridged Native Assets. Any contract that implements the SRC-8 standard MUST implement the SRC-7 and SRC-20 standards.

Motivation

The SRC-8 standard seeks to enable relevant data for bridged assets on the Fuel Network. This data includes the origin chain, address, ID, decimals, and any arbitrary data. All metadata queries are done through a single function to facilitate cross-contract calls.

Prior Art

The use of generic metadata for Native Assets is defined in the SRC-7 standard. This standard integrates into the existing SRC-7 standard.

Specification

Asset Creation

The SubId of the asset MUST be the digest of the sha256(origin_chain_id, origin_asset_address, origin_asset_id) hash where:

  • origin_chain_id is a String of the chain ID where the asset was originally minted.
  • origin_asset_address is a b256 of the asset's address on the chain where the asset was originally minted.
  • origin_asset_id is a b256 of the asset's ID such as an NFT's ID on the chain where the asset was originally minted. IF there is no ID, b256::zero() SHALL be used.

SRC-20 Metadata

Any bridged assets MUST use the name and symbol of the asset on the chain where the asset was originally minted.

SRC-7 Metadata

bridged:chain

The key bridged:chain SHALL return an String variant of the chain ID where the asset was originally minted.

bridged:address

The key bridged:address SHALL return a B256 variant of the asset's address on the chain where the asset was originally minted. Native assets of a chain that do not have an address such as Ether on Ethereum SHALL use b256::zero().

bridged:id

The key bridged:id MAY return a B256 variant of the asset's ID such as an NFT's ID on the chain where the asset was originally minted. IF there is no ID, None SHALL be returned.

bridged:decimals

The key bridged:decimals MAY return an Int variant of the asset's decimals on the chain where the asset was originally minted. IF there are no decimals, None SHALL be returned.

Rationale

The SRC-8 standard should allow for data on any bridged assets on the Fuel Network. This standard builds off existing standards and should allow other contracts to query any relevant information on the bridged asset.

Backwards Compatibility

This standard is compatible with Fuel's Native Assets, the SRC-20 standard, and the SRC-7 standard.

The standard is also compatible with both tokens and NFTs native to other ecosystems by introducing a token ID element of the original chain.

Security Considerations

This standard does not call external contracts, nor does it define any mutations of the contract state.

Example

impl SRC7 for Contract {
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        if (asset != AssetId::default()) {
            return Option::None;
        }

        match key {
            String::from_ascii_str("bridged:chain") => {
                Option::Some(String::from_ascii_str("1"))
            },
            String::from_ascii_str("bridged:address") => {
                let origin_asset_address = b256::zero();
                Option::Some(Metadata::B256(origin_asset_address))
            },
            String::from_ascii_str("bridged:id") => {
                let origin_asset_id = b256::zero();
                Option::Some(Metadata::B256(origin_asset_id))
            },
            String::from_ascii_str("bridged:decimals") => {
                Option::Some(Metadata::Int(1))
            },
            _ => Option::None,
        }
    }
}

impl SRC20 for Contract {
    fn total_assets() -> u64 {
        1
    }

    fn total_supply(asset: AssetId) -> Option<u64> {
        match asset {
            AssetId::default() => Option::Some(1),
            _ => Option::None,
        }
    }

    fn name(asset: AssetId) -> Option<String> {
        match asset {
            AssetId::default() => Option::Some(String::from_ascii_str("Name")),
            _ => Option::None,
        }
    }

    fn symbol(asset: AssetId) -> Option<String> {
        match asset {
            AssetId::default() => Option::Some(String::from_ascii_str("Symbol")),
            _ => Option::None,
        }
    }

    fn decimals(asset: AssetId) -> Option<u8> {
        match asset {
            AssetId::default() => Option::Some(0u8),
            _ => Option::None,
        }
    }
}

SRC-9: Native Asset

The following standard attempts to define the keys of relevant on-chain metadata for any Native Assets. Any contract that implements the SRC-9 standard MUST implement the SRC-7 and SRC-20 standards. This is a living standard where revisions may be made as the ecosystem evolves.

Motivation

The SRC-9 standard seeks to enable relevant data for assets on the Fuel Network. This data may include images, text, contact, or all of the above. All metadata queries are done through a single function to facilitate cross-contract calls.

Prior Art

The use of generic metadata for Native Assets is defined in the SRC-7 standard. This standard integrates into the existing SRC-7 standard.

Specification

The following keys are reserved for the SRC-9 standard. Use of the keys should follow the SRC-9 specification.

All keys SHALL use snake case.

Social

The social prefix SHALL be used for any social media platform and SHALL return usernames.

Any social media metadata keys SHALL follow the following syntax social:site where:

  • The social keyword must be prepended to denote this is a social platform
  • The site keyword must be the website or platform of the social

social:discord

The key social:discord SHALL return a String variant of a username for the Discord platform.

social:facebook

The key social:facebook SHALL return a String variant of a username for the Facebook platform.

social:farcaster

The key social:farcaster SHALL return a String variant of a username for the Farcaster platform.

social:friend.tech

The key social:friend.tech SHALL return a String variant of a username for the Friend.tech platform.

social:github

The key social:github SHALL return a String variant of a username for the Github platform.

social:instagram

The key social:instagram SHALL return a String variant of a username for the Instagram platform.

social:lens

The key social:lens SHALL return a String variant of a username for the Lens Protocol.

social:linkedin

The key social:linkedin SHALL return a String variant of a username for the LinkedIn platform.

social:reddit

The key social:reddit SHALL return a String variant of a username for the Reddit platform.

social:signal

The key social:signal SHALL return a String variant of a username for the Signal platform.

social:telegram

The key social:telegram SHALL return a String variant of a username for the Telegram platform.

social:tiktok

The key social:tiktok SHALL return a String variant of a username for the TikTok platform.

social:x

The key social:x SHALL return a String variant of a username for the X or formerly Twitter platform.

social:wechat

The key social:wechat SHALL return a String variant of a username for the WeChat platform.

social:whatsapp

The key social:whatsapp SHALL return a String variant of a username for the WhatsApp platform.

social:youtube

The key social:youtube SHALL return a String variant of a username for the YouTube platform.

Contact

The contact prefix SHALL be used for any contact information on a particular project's team for an asset.

Any contact information metadata keys SHALL follow the following syntax contract:type where:

  • The contact keyword must be prepended to denote this is contact information
  • The type keyword must be the method of contact

The key SHALL use snake case.

contact:email

The key contact:email SHALL return a String variant of an email.

contact:mailing

The key contact:mailing SHALL return a String variant of a mailing address. All mailing addresses MUST follow the UPU addressing format.

contact:phone

The key contact:phone SHALL return a String variant of a phone number. All phone numbers SHALL follow the E.164 standard.

contact:company

The key contact:company SHALL return a String variant of a company name.

The link prefix SHALL be used for any external webpage hyperlink associated with an asset.

Any external webpage metadata keys SHALL follow the following syntax link:site where:

  • The link keyword must be prepended to denote this is an external webpage
  • The site keyword must be an external website

link:home

The key link:home SHALL return a String variant of the asset's project homepage.

link:contact

The key link:contact SHALL return a String variant of the asset's project contact information webpage.

link:docs

The key link:docs SHALL return a String variant of the asset's project documentation webpage.

link:forum

The key link:forum SHALL return a String variant of the asset's project forum webpage.

link:blog

The key link:blog SHALL return a String variant of the asset's project blog.

link:linktree

The key link:linktree SHALL return a String variant of the asset's project linktree information webpage.

Resources

The res prefix SHALL be used for any resources or general information on an asset.

Any resource metadata keys SHALL follow the following syntax rec:type where:

  • The res keyword must be prepended to denote this is a resource
  • The type keyword must be the type of resource

res:license

The key res:license SHALL return a String variant of the asset's project license.

res:tos

The key res:tos SHALL return a String variant of the asset's project Terms of Service.

res:author

The key res:author SHALL return a String variant of the asset's project author. This MAY be a full name or pseudonym.

res:about

The key res:about SHALL return a String variant about the asset's project up to 2048 characters.

res:description

The key res:description SHALL return a String variant describing the asset's project up to 256 characters.

res:date

The key res:date SHALL return a Int variant of a UNIX timestamp.

res:block

The key res:block SHALL return a Int variant of a block number.

Images

The image prefix SHALL be used for any image files associated with a singular asset.

Any image metadata keys SHALL follow the following syntax image:type where:

  • The image keyword must be prepended to denote this is an image
  • The type keyword must be the file type of the image

image:svg

The key image:svg SHALL return a String variant of an SVG image.

image:png

The key image:png SHALL return a String variant of a URI for a PNG image.

image:jpeg

The key image:jpeg SHALL return a String variant of a URI for a JPEG image.

image:webp

The key image:webp SHALL return a String variant of a URI for a WebP image.

image:gif

The key image:gif SHALL return a String variant of a URI for a GIF image.

image:heif

The key image:heif SHALL return a String variant of a URI for a HEIF image.

Video

The video prefix SHALL be used for any video files associated with a singular asset.

Any video metadata keys SHALL follow the following syntax video:type where:

  • The video keyword must be prepended to denote this is a video
  • The type keyword must be the file type of the video

video:mp4

The key video:mp4 SHALL return a String variant of a URI for an MP4 video.

video:webm

The key video:webm SHALL return a String variant of a URI for a WebM video.

video:m4v

The key video:m4v SHALL return a String variant of a URI for a M4V video.

video:ogv

The key video:ogv SHALL return a String variant of a URI for an OGV video.

video:ogg

The key video:ogg SHALL return a String variant of a URI for an OGG video.

Audio

The audio prefix SHALL be used for any audio files associated with a singular asset.

Any audio metadata keys SHALL follow the following syntax audio:type where:

  • The audio keyword must be prepended to denote this is audio metadata
  • The type keyword must be the file type of the audio

audio:mp3

The key audio:mp3 SHALL return a String variant of a URI for an MP3 file.

audio:wav

The key audio:wav SHALL return a String variant of a URI for a WAV file.

audio:oga

The key audio:oga SHALL return a String variant of a URI for an OGA file.

Media

The media prefix SHALL be used for any media associated with a particular singular asset.

Any media metadata keys SHALL follow the following syntax media:type where:

  • The media keyword must be prepended to denote this is a video
  • The type keyword must be the file type of the media

media:gltf

The key media:gltf SHALL return a String variant of a URI for a glTF file.

media:glb

The key media:glb SHALL return a String variant of a URI for a GLB file.

Logos

The logo prefix SHALL be used for any images associated with a particular asset or project.

Any logo metadata keys SHALL follow the following syntax logo:type where:

  • The logo keyword must be prepended to denote this is a logo
  • The type keyword must be the type of logo

logo:svg

The key logo:svg SHALL return a String variant of an SVG image of a logo.

logo:svg_light

The key logo:svg_light SHALL return a String variant of an SVG image of a logo for light themes.

logo:svg_dark

The key logo:svg_dark SHALL return a String variant of an SVG image of a logo for dark themes.

logo:small_light

The key logo:small_light SHALL return a String variant of a URI for a 32x32 PNG image of a logo for light themes.

logo:small_dark

The key logo:small_dark SHALL return a String variant of a URI for a 32x32 PNG image of a logo for dark themes.

logo:medium_light

The key logo:medium_light SHALL return a String variant of a URI for a 256x256 PNG image of a logo for light themes.

logo:medium_dark

The key logo:medium_dark SHALL return a String variant of a URI for a 256x256 PNG image of a logo for dark themes.

logo:large_light

The key logo:large_light SHALL return a String variant of a URI for a 1024x1024 PNG image of a logo for light themes.

logo:large_dark

The key logo:large_dark SHALL return a String variant of a URI for a 1024x1024 PNG image of a logo for dark themes.

Attributes

The attr prefix SHALL be used for any attributes associated with a singular asset.

Any attribute metadata keys SHALL follow the following syntax attr:type where:

  • The attr keyword must be prepended to denote this is an attribute
  • The type keyword must be the type of attribute

There are no standardized types of attributes. Example: attr:eyes.

Rationale

The SRC-9 standard should allow for standardized keys for metadata on the Fuel Network. This standard builds off existing standards and should allow other contracts to query any relevant information on the asset.

Backwards Compatibility

This standard is compatible with Fuel's Native Assets, the SRC-20 standard, and the SRC-7 standard.

Security Considerations

This standard does not call external contracts, nor does it define any mutations of the contract state.

Example

impl SRC7 for Contract {
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        if (asset != AssetId::default()) {
            return Option::None;
        }

        match key {
            String::from_ascii_str("social:x") => {
                let social = String::from_ascii_str("fuel_network");
                Option::Some(Metadata::String(social))
            },
            _ => Option::None,
        }
    }
}

SRC-10: Native Bridge

The following standard allows for the implementation of a standard API for Native Bridges using the Sway Language. The standardized design has the bridge contract send a message to the origin chain to register which token it accepts to prevent a loss of funds.

Motivation

A standard interface for bridges intends to provide a safe and efficient bridge between the settlement or canonical chain and the Fuel Network.

Prior Art

The standard is centered on Fuel’s Bridge Architecture. Fuel's bridge system is built on a message protocol that allows to send (and receive) messages between entities located in two different blockchains.

The following standard takes reference from the FungibleBridge ABI defined in the fuel-bridge repository.

Specification

The following functions MUST be implemented to follow the SRC-10; Native Bridge Standard:

Required Functions

fn process_message(message_index: u64)

The process_message() function accepts incoming deposit messages from the canonical chain and issues the corresponding bridged asset.

  • This function MUST parse a message at the given message_index index.
  • This function SHALL mint an asset that follows the SRC-8; Bridged Asset Standard.
  • This function SHALL issue a refund if there is an error in the bridging process.

fn withdraw(to_address: b256)

The withdraw() function accepts and burns a bridged Native Asset on Fuel and sends a message to the bridge contract on the canonical chain to release the originally deposited tokens to the to_address address.

  • This function SHALL send a message to the bridge contract to release the bridged tokens to the to_address address on the canonical chain.
  • This function MUST ensure the asset's AssetId sent in the transaction matches a bridged asset.
  • This function SHALL burn all coins sent in the transaction.

fn claim_refund(to_address: b256, token_address: b256, token_id: b256, gateway_contract: b256)

The claim_refund() function is called if something goes wrong in the bridging process and an error occurs. It sends a message to the gateway_contract contract on the canonical chain to release the token_address token with token id token_id to the to_address address.

  • This function SHALL send a message to the gateway_contract contract to release the token_address token with id token_id to the to_address address on the canonical chain.
  • This function MUST ensure a refund was issued.

Required Data Types

DepositType

The DepositType enum describes whether the bridged deposit is made to a address, contract, or contract and contains additional metadata. There MUST be the following variants in the DepositType enum:

Address: ()

The Address variant MUST represent when the deposit is made to an address on the Fuel chain.

Contract: ()

The Contract variant MUST represent when the deposit is made to an contract on the Fuel chain.

ContractWithData: ()

The ContractWithData variant MUST represent when the deposit is made to an contract and contains additional metadata for the Fuel chain.

Example Deposit Type
pub enum DepositType {
    Address: (),
    Contract: (),
    ContractWithData: (),
}

DepositMessage

The following describes a struct that encapsulates various deposit message metadata to a single type. There MUST be the following fields in the DepositMessage struct:

amount: u256

The amount field MUST represent the number of tokens.

from: b256

The from field MUST represent the bridging user’s address on the canonical chain.

to: Identity

The to field MUST represent the bridging target destination Address or ContractId on the Fuel Chain.

token_address: b256

The token_address field MUST represent the bridged token's address on the canonical chain.

token_id: b256

The token_id field MUST represent the token's ID on the canonical chain. The b256::zero() MUST be used if this is a fungible token and no token ID exists.

decimals: u8

The decimals field MUST represent the bridged token's decimals on the canonical chain.

deposit_type: DepositType

The deposit_type field MUST represent the type of bridge deposit made on the canonical chain.

Example Deposit Message
pub struct DepositMessage {
    pub amount: b256,
    pub from: b256,
    pub to: Identity,
    pub token_address: b256,
    pub token_id: b256,
    pub decimals: u8,
    pub deposit_type: DepositType,
}

MetadataMessage

The following describes a struct that encapsulates the metadata of token on the canonical chain to a single type. There MUST be the following fields in the MetadataMessage struct:

token_address: b256

The token_address field MUST represent the bridged token's address on the canonical chain.

token_id: b256

The token_id field MUST represent the token's ID on the canonical chain. The b256::zero() MUST be used if this is a fungible token and no token ID exists.

name: String

The name field MUST represent the bridged token's name field on the canonical chain.

symbol: String

The symbol field MUST represent the bridged token's symbol field on the canonical chain.

Example Metadata Message
pub struct MetadataMessage {
    pub token_address: b256,
    pub token_id: b256,
    pub name: String,
    pub symbol: String,
}

Required Standards

Any contract that implements the SRC-10; Native Bridge Standard MUST implement the SRC-8; Bridged Asset Standard for all bridged assets.

Rationale

The SRC-10; Native Bridge Standard is designed to standardize the native bridge interface between all Fuel instances.

Backwards Compatibility

This standard is compatible with the SRC-20 and SRC-8 standards.

Example ABI

abi SRC10 {
     fn process_message(message_index: u64);
     fn withdraw(to_address: b256);
     fn claim_refund(to_address: b256, token_address: b256, token_id: b256, gateway_contract: b256);
}

SRC-11: Security Information

The following standard allows for contract creators to make communication information readily available to everyone, with the primary purpose of allowing white hat hackers to coordinate a bug-fix or securing of funds.

Motivation

White hat hackers may find bugs or exploits in contracts that they want to report to the project for safeguarding of funds. It is not immediately obvious from a ContractId, who the right person to contact is. This standard aims to make the process of bug reporting as smooth as possible.

Prior Art

The security.txt library for Solana has explored this idea. This standard takes inspiration from the library, with some changes.

Specification

Security Information Type

The following describes the SecurityInformation type.

  • The struct MAY contain None for Option<T> type fields, if they are deemed unnecessary.
  • The struct MUST NOT contain empty String or Vec fields.
  • The struct MAY contain a URL or the information directly for the following fields: project_url, policy, encryption, source_code, auditors, acknowledgments, additional_information.
  • The struct MUST contain the information directly for the following fields: name, contact_information, preferred_languages, source_release, and source_revision.
  • The struct MUST contain at least one item in the preferred_languages field's Vec, if it is not None. Furthermore, the string should only contain the ISO 639-1 language code and nothing else.
  • The struct MUST contain at least one item in the contact_information field's Vec. Furthermore, the string should follow the following format <contact_type>:<contact_information>. Where contact_type describes the method of contact (e.g. email or discord) and contact_information describes the information needed to contact (e.g. example@example.com or @EXAMPLE).

name: String

The name of the project that the contract is associated with.

project_url: Option<String>

The website URL of the project that the contract is associated with.

contact_information: Vec<String>

A list of contact information to contact developers of the project. Should be in the format <contact_type>:<contact_information>. You should include contact types that will not change over time.

policy: String

Text describing the project's security policy, or a link to it. This should describe what kind of bounties your project offers and the terms under which you offer them.

preferred_languages: Option<Vec<String>>

A list of preferred languages ISO 639-1. If the field is not None, it MUST contain at least one item.

encryption: Option<String>

A PGP public key block (or similar) or a link to one.

source_code: Option<String>

A URL to the project's source code.

source_release: Option<String>

The release identifier of this build, ideally corresponding to a tag on git that can be rebuilt to reproduce the same binary. 3rd party build verification tools will use this tag to identify a matching GitHub release.

source_revision: Option<String>

The revision identifier of this build, usually a git commit hash that can be rebuilt to reproduce the same binary. 3rd party build verification tools will use this tag to identify a matching GitHub release.

auditors: Option<Vec<String>>

A list of people or entities that audited this smart contract, or links to pages where audit reports are hosted. Note that this field is self-reported by the author of the program and might not be accurate.

acknowledgments: Option<String>

Text containing acknowledgments to security researchers who have previously found vulnerabilities in the project, or a link to it.

additional_information: Option<String>

Text containing any additional information you want to provide, or a link to it.

Required Functions

The following function MUST be implemented to follow the SRC-11 standard.

fn security_information() -> SecurityInformation;

This function takes no input parameters and returns a struct containing contact information for the project owners, information regarding the bug bounty program, other information related to security, and any other information that the developers find relevant.

  • This function MUST return accurate and up to date information.
  • This function's return values MUST follow the specification for the SecurityInformation type.
  • This function MUST NOT revert under any circumstances.

Rationale

The return structure discussed covers most information that may want to be conveyed regarding the security of the contract, with an additional field to convey any additional information. This should allow easy communication between the project owners and any white hat hackers if necessary.

Backwards Compatibility

This standard does not face any issues with backward compatibility.

Security Considerations

The information is entirely self reported and as such might not be accurate. Accuracy of information cannot be enforced and as such, anyone using this information should be aware of that.

Example ABI

abi SRC11 {
    #[storage(read)]
    fn security_information() -> SecurityInformation;
}

Example Implementation

Hard coded information

A basic implementation of the security information standard demonstrating how to hardcode information to be returned.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src11-security-information/hardcoded-information/src/main.sw' -->

Variable information

A basic implementation of the security information standard demonstrating how to return variable information that can be edited to keep it up to date. In this example only the contact_information field is variable, but the same method can be applied to any field which you wish to update.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src11-security-information/variable-information/src/main.sw' -->

SRC-12: Contract Factory

The following standard allows for the implementation of a standard ABI for Contract Factories using the Sway Language. The standardized design designates how verification of newly deployed child contracts are handled.

Motivation

A standard interface for Contract Factories provides a safe and effective method of ensuring contracts can verify the validity of another contract as a child of a factory. This is critical on the Fuel Network as contracts cannot deploy other contracts and verification must be done after deployment.

Prior Art

A Contract Factory is a design where a template contract is used and deployed repeatedly with different configurations. These configurations are often minor changes such as pointing to a different asset. All base functionality remains the same.

On Fuel, contracts cannot deploy other contracts. As a result, a Contract Factory on Fuel must register and verify that the bytecode root of a newly deployed child contract matches the expected bytecode root.

When changing something such as a configurable in Sway, the bytecode root is recalculated. The Bytecode Library has been developed to calculate the bytecode root of a contract with different configurables.

Specification

The following functions MUST be implemented to follow the SRC-12; Contract Factory Standard:

Required Functions

fn register_contract(child_contract: ContractId, configurables: Option<Vec<(u64, Vec<u8>)>>) -> Result<b256, str>

The register_contract() function verifies that a newly deployed contract is the child of a contract factory.

  • This function MUST verify that the bytecode root of the child_contract contract matches the expected bytecode root.
  • This function MUST calculate the bytecode root IF configurables is Some.
  • This function MUST not revert.
  • This function MUST return a Result containing the b256 bytecode root of the newly registered contract or an str error message.
  • This function MAY add arbitrary conditions checking a contract factory child’s validity, such as verifying storage variables or initialized values.

fn is_valid(child_contract: ContractId) -> bool

The is_valid() function returns a boolean representing the state of whether a contract is registered as a valid child of the contract factory.

  • This function MUST return true if this is a valid and registered child, otherwise false.

fn factory_bytecode_root() -> Option<b256>

The factory_bytecode_root() function returns the bytecode root of the default template contract.

  • This function MUST return the bytecode root of the template contract.

Optional Functions

The following are functions that may enhance the use of the SRC-12 standard but ARE NOT required.

fn get_contract_id(configurables: Option<Vec<(u64, Vec<u8>)>>) -> Option<ContractId>

The get_contract_id() function returns a registered contract factory child contract with specific implementation details specified by configurables.

This function MUST return Some(ContractId) IF a contract that follows the specified configurables has been registered with the SRC-12 Contract Factory contract, otherwise None.

Rationale

The SRC-12; Contract Factory Standard is designed to standardize the contract factory design implementation interface between all Fuel instances.

Backwards Compatibility

There are no other standards that the SRC-12 requires compatibility.

Security Considerations

This standard takes into consideration child contracts that are deployed with differentiating configurable values, however individual contract behaviours may be dependent on storage variables. As storage variables may change after the contract has been registered with the SRC-12 compliant contract, the standard suggests to check these values upon registration however it is not enforced.

Example ABI

abi SRC12 {
    #[storage(read, write)]
    fn register_contract(child_contract: ContractId, configurables: Option<Vec<(u64, Vec<u8>)>>) -> Result<b256, str>;
    #[storage(read)]
    fn is_valid(child_contract: ContractId) -> bool;
    #[storage(read)]
    fn factory_bytecode_root() -> Option<b256>;
}

abi SRC12_Extension {
    #[storage(read)]
    fn get_contract_id(configurables: Option<Vec<(u64, Vec<u8>)>>) -> Option<ContractId>;
}

Example Implementation

With Configurables

Example of the SRC-12 implementation where contract deployments contain configurable values that differentiate the bytecode root from other contracts with the same bytecode.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src12-contract-factory/with_configurables/src/with_configurables.sw' -->

Without Configurables

Example of the SRC-12 implementation where all contract deployments are identical and thus have the same bytecode and root.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src12-contract-factory/without_configurables/src/without_configurables.sw' -->

SRC-13: Soulbound Address

The following standard allows for the implementation of Soulbound Address on the Fuel Network. Soulbound Assets are Native Assets sent to the Soulbound Address and cannot be transferred. As Native Assets on the Fuel Network do not require approvals to be spent, any asset sent to an Address may be transferable. The SRC-13 standard provides a predicate interface to lock Native Assets as soulbound.

Motivation

This standard enables soulbound assets on Fuel and allows external applications to query and provide soulbound assets, whether that be decentralized exchanges, wallets, or other external applications.

Prior Art

Native Assets on the Fuel Network do not require the implementation of certain functions such as transfer or approval. This is done directly within the FuelVM and there is no smart contract that requires updating of balances. As such, any assets sent to an Address may be spendable and ownership of that asset may be transferred. For any soulbound assets, spending must be restricted.

Predicates are programs that return a Boolean value and which represent ownership of some resource upon execution to true. All predicates evaluate to an Address based on their bytecode root. A predicate must evaluate to true such that the assets may be spent.

The SRC-13 Soulbound Asset Standard naming pays homage to the ERC-5192: Minimal Soulbound NFTs seen on Ethereum. While there is functionality we may use as a reference, it is noted that Fuel's Native Assets are fundamentally different than Ethereum's tokens.

Specification

Overview

To ensure that some asset shall never be spent, we must apply spending conditions. This can be done with Predicates on Fuel. Any asset sent to a Predicate Address shall never be spent if the predicate never evaluates to true.

We must also ensure every Address on Fuel has its own Predicate. This can be guaranteed by using a configurable where an Address is defined.

Definitions

  • Soulbound Address Predicate - The resulting predicate which owns assets on behalf of an Address.
  • Soulbound Address - The computed Address of the Soulbound Asset Predicate.
  • Soulbound Asset - Any Native Asset sent to the Soulbound Address.

Soulbound Address Predicate Specification

  • The Soulbound Address Predicate SHALL never spend the assets sent to its computed predicate Address or Soulbound Address.
  • The Soulbound Address Predicate SHALL encode an Address of which it represents the soulbound address.

Below we define the Soulbound Address Predicate where ADDRESS MUST be replaced with the Address of which the Soulbound Address Predicate represents.

predicate;

configurable {
    ADDRESS: Address = Address::from(0x0000000000000000000000000000000000000000000000000000000000000000),
}

fn main() -> bool {
    asm (address: ADDRESS) { address: b256 };
    false
}

Soulbound Address

The Soulbound Address is the Soulbound Address Predicate's predicate address. A predicate's address(the bytecode root) is defined here.

The Soulbound Address may be computed from the Soulbound Address Predicate's bytecode both on-chain or off-chain. For off-chain computation, please refer to the fuels-rs predicate docs. For on-chain computation, please refer to Sway-Lib's Bytecode Library.

Rationale

On the Fuel Network, the process for sending any Native Assets is the same and does not require any approval. This means that any assets sent to an Address may be spendable and does not require any external spending conditions. In the case of a soulbound asset, we need to ensure the asset cannot be spent.

Backwards Compatibility

This standard is compatible with Fuel's Native Assets and the SRC-20 standard.

Security Considerations

This standard does not introduce any security concerns, as it does not call external contracts, nor does it define any mutations of the contract state.

It should however be noted that any Native Asset on the Fuel Network is not a Soulbound Asset until it is sent to a Soulbound Address.

Example

The following example shows the Soulbound Address Predicate for the 0xe033369a522e3cd2fc19a5a705a7f119938027e8e287c0ec35b784e68dab2be6 Address.

The resulting Soulbound Address is 0x7f28a538d06788a3d98bb72f4b41012d86abc4b0369ee5dedf56cfbaf245d609. Any Native Assets sent to this address will become Soulbound Assets.

predicate;

configurable {
    ADDRESS: Address = Address::from(0xe033369a522e3cd2fc19a5a705a7f119938027e8e287c0ec35b784e68dab2be6),
}

fn main() -> bool {
    asm (address: ADDRESS) { address: b256 };
    false
}

SRC-14: Simple Upgradeable Proxies

The following proposes a standard for simple upgradeable proxies.

Motivation

We seek to standardize a proxy implementation to improve developer experience and enable tooling to automatically deploy or update proxies as needed.

Prior Art

This OpenZeppelin blog post is a good survey of the state of the art at this time.

Proxy designs fall into three essential categories:

  1. Immutable proxies which are lightweight clones of other contracts but can't change targets
  2. Upgradeable proxies such as UUPS which store a target in storage and delegate all calls to it
  3. Diamonds which are both upgradeable and can point to multiple targets on a per method basis

This document falls in the second category. We want to standardize the implementation of simple upgradeable pass-through contracts.

The FuelVM provides an LDC instruction that is used by Sway's std::execution::run_external to provide a similar behavior to EVM's delegatecall and execute instructions from another contract while retaining one's own storage context. This is the intended means of implementation of this standard.

Specification

Required Behavior

The proxy contract MUST maintain the address of its target in its storage at slot 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55 (equivalent to sha256("storage_SRC14_0")). It SHOULD base other proxy specific storage fields in the SRC14 namespace to avoid collisions with target storage. It MAY have its storage definition overlap with that of its target if necessary.

The proxy contract MUST delegate any method call not part of its interface to the target contract.

This delegation MUST retain the storage context of the proxy contract.

Required Public Functions

The following functions MUST be implemented by a proxy contract to follow the SRC-14 standard:

fn set_proxy_target(new_target: ContractId);

If a valid call is made to this function it MUST change the target contract of the proxy to new_target. This method SHOULD implement access controls such that the target can only be changed by a user that possesses the right permissions (typically the proxy owner).

fn proxy_target() -> Option<ContractId>;

This function MUST return the target contract of the proxy as Some. If no proxy is set then None MUST be returned.

Optional Public Functions

The following functions are RECOMMENDED to be implemented by a proxy contract to follow the SRC-14 standard:

fn proxy_owner() -> State;

This function SHALL return the current state of ownership for the proxy contract where the State is either Uninitialized, Initialized, or Revoked. State is defined in the SRC-5; Ownership Standard.

Rationale

This standard is meant to provide simple upgradeability, it is deliberately minimalistic and does not provide the level of functionality of diamonds.

Unlike in UUPS, this standard requires that the upgrade function is part of the proxy and not its target. This prevents irrecoverable updates if a proxy is made to point to another proxy and no longer has access to upgrade logic.

Backwards Compatibility

SRC-14 is intended to be compatible with SRC-5 and other standards of contract functionality.

As it is the first attempt to standardize proxy implementation, we do not consider interoperability with other proxy standards.

Security Considerations

Permissioning proxy target changes is the primary consideration here. Use of the SRC-5; Ownership Standard is discouraged. If both the target and proxy contracts implement the SRC-5 standard, the owner() function in the target contract is unreachable through the proxy contract. Use of the proxy_owner() function in the proxy contract should be used instead.

Example ABI

abi SRC14 {
    #[storage(read, write)]
    fn set_proxy_target(new_target: ContractId);
    #[storage(read)]
    fn proxy_target() -> Option<ContractId>;
}

abi SRC14Extension {
    #[storage(read)]
    fn proxy_owner() -> State;
}

Example Implementation

Minimal Proxy

Example of a minimal SRC-14 implementation with no access control.

```sway\ncontract;

use std::execution::run_external;
use standards::src14::{SRC14, SRC14_TARGET_STORAGE};

storage {
    SRC14 {
        /// The [ContractId] of the target contract.
        ///
        /// # Additional Information
        ///
        /// `target` is stored at sha256("storage_SRC14_0")
        target in 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55: ContractId = ContractId::zero(),
    },
}

impl SRC14 for Contract {
    #[storage(read, write)]
    fn set_proxy_target(new_target: ContractId) {
        storage::SRC14.target.write(new_target);
    }

    #[storage(read)]
    fn proxy_target() -> Option<ContractId> {
        storage::SRC14.target.try_read()
    }
}

#[fallback]
#[storage(read)]
fn fallback() {
    // pass through any other method call to the target
    run_external(storage::SRC14.target.read())
}\n```

Owned Proxy

Example of a SRC-14 implementation that also implements proxy_owner().

```sway\ncontract;

use std::execution::run_external;
use standards::src5::{AccessError, State};
use standards::src14::{SRC14, SRC14_TARGET_STORAGE, SRC14Extension};

/// The owner of this contract at deployment.
#[allow(dead_code)]
const INITIAL_OWNER: Identity = Identity::Address(Address::zero());

storage {
    SRC14 {
        /// The [ContractId] of the target contract.
        ///
        /// # Additional Information
        ///
        /// `target` is stored at sha256("storage_SRC14_0")
        target in 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55: ContractId = ContractId::zero(),
        /// The [State] of the proxy owner.
        owner: State = State::Initialized(INITIAL_OWNER),
    },
}

impl SRC14 for Contract {
    #[storage(read, write)]
    fn set_proxy_target(new_target: ContractId) {
        only_owner();
        storage::SRC14.target.write(new_target);
    }

    #[storage(read)]
    fn proxy_target() -> Option<ContractId> {
        storage::SRC14.target.try_read()
    }
}

impl SRC14Extension for Contract {
    #[storage(read)]
    fn proxy_owner() -> State {
        storage::SRC14.owner.read()
    }
}

#[fallback]
#[storage(read)]
fn fallback() {
    // pass through any other method call to the target
    run_external(storage::SRC14.target.read())
}

#[storage(read)]
fn only_owner() {
    require(
        storage::SRC14
            .owner
            .read() == State::Initialized(msg_sender().unwrap()),
        AccessError::NotOwner,
    );
}\n```

SRC-15: Off-Chain Native Asset Metadata

The following standard attempts to define arbitrary metadata for any Native Asset that is not required by other contracts onchain, in a stateless manner. Any contract that implements the SRC-15 standard MUST implement the SRC-20 standard.

Motivation

The SRC-15 standard seeks to enable data-rich assets on the Fuel Network while maintaining a stateless solution. All metadata queries are done off-chain using the indexer.

Prior Art

The SRC-7 standard exists prior to the SRC-15 standard and is a stateful solution. The SRC-15 builds off the SRC-7 standard by using the Metadata enum however provides a stateless solution.

The use of generic metadata was originally found in the Sway-Lib's NFT Library which did not use Fuel's Native Assets. This library has since been deprecated.

A previous definition for a metadata standard was written in the original edit of the now defunct SRC-721. This has since been replaced with the SRC-20 standard as SubId was introduced to enable multiple assets to be minted from a single contract.

Specification

Metadata Type

The Metadata enum from the SRC-7 standard is also used to represent the metadata in the SRC-15 standard.

Logging

The following logs MUST be implemented and emitted to follow the SRC-15 standard. Logging MUST be emitted from the contract which minted the asset.

SRC15MetadataEvent

The SRC15MetadataEvent MUST be emitted at least once for each distinct piece of metadata. The latest emitted SRC15MetadataEvent is determined to be the current metadata.

There SHALL be the following fields in the SRC15MetadataEvent struct:

  • asset: The asset field SHALL be used for the corresponding AssetId for the metadata.
  • metadata: The metadata field SHALL be used for the corresponding Metadata which represents the metadata of the asset.

Example:

pub struct SRC15MetadataEvent {
    pub asset: AssetId,
    pub metadata: Metadata,
}

Rationale

The SRC-15 standard allows for data-rich assets in a stateless manner by associating an asset with some metadata that may later be fetched by the indexer.

Backwards Compatibility

This standard is compatible with Fuel's Native Assets and the SRC-20 standard. This standard is also compatible with the SRC-7 standard which defines a stateful solution. It also maintains compatibility with existing standards in other ecosystems.

Security Considerations

When indexing for SRC-15 metadata, developers should confirm that the contract that emitted the SRC15MetadataEvent is also the contract that minted the asset that the metadata associates with. Additionally, restrictions via access control on who may emit the Metadata should be considered.

Example Implementation

Single Native Asset

Example of the SRC-15 implementation where metadata exists for only a single asset with one SubId.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src15-offchain-metadata/single_asset/src/single_asset.sw' -->

Multi Native Asset

Example of the SRC-15 implementation where metadata exists for multiple assets with differing SubId values.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src15-offchain-metadata/multi_asset/src/multi_asset.sw' -->

SRC-16: Typed Structured Data

The following standard sets out to standardize encoding and hashing of typed structured data. This enables secure off-chain message signing with human-readable data structures.

Motivation

As the Fuel ecosystem expands, there's an increasing need for applications to handle complex, human-readable data structures rather than raw bytes. When users sign messages or transactions, they should be able to clearly understand what they're signing, whether it's a simple asset transfer, or a complex DeFi interaction. Without a standard method for hashing structured data, developers risk implementing their own solutions, which could lead to confusion or compromise security. This standard provides a secure and consistent way to handle encoding and hashing of structured data, ensuring both safety and usability within ecosystem.

This standard aims to:

  • Provide a secure, standardized method for hashing structured data
  • Enable clear presentation of structured data for user verification during signing
  • Support complex data types that mirror Sway structs
  • Enable domain separation to prevent cross-protocol replay attacks
  • Define a consistent encoding scheme for structured data types
  • Remain stateless, not requiring any storage attributes to enable use across all Fuel program types.

Prior Art

This standard uses ideas from Ethereum's EIP-712 standard, adapting its concepts for the Fuel ecosystem. EIP-712 has proven successful in enabling secure structured data signing for applications like the various browser based wallets and signers that are utilized throughout various DeFi protocols.

Specification

Definition of Typed Structured Data 𝕊

The set of structured data 𝕊 consists of all instances of struct types that can be composed from the following types:

Atomic Types:

u8 to u256
bool
b256 (hash)

Dynamic Types:

Bytes   // Variable-length byte sequences
String  // Variable-length strings

Reference Types:

Arrays (both fixed size and dynamic) Structs (reference to other struct types)

Example struct definition:

struct Mail {
    from: Address,
    to: Address,
    contents: String,
}

Domain Separator Encoding

The domain separator provides context for the signing operation, preventing cross-protocol replay attacks. It is computed as hash_struct(domain) where domain is defined as:

pub struct SRC16Domain {
    name: String,                   // The protocol name (e.g., "MyProtocol")
    version: String,                // The protocol version (e.g., "1")
    chain_id: u64,                  // The Fuel chain ID
    verifying_contract: ContractId, // The contract id that will verify the signature
}

The chain_id field is a u64 that must be encoded by left-padding with zeros and packed in big-endian order to fill a 32-byte value.

The domain separator encoding follows this scheme:

  • Add SRC16_DOMAIN_TYPE_HASH
  • Add Keccak256 hash of name string
  • Add Keccak256 hash of version string
  • Add chain ID as 32-byte big-endian
  • Add verifying contract id as 32 bytes

Type Encoding

Each struct type is encoded as name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")" where each member is written as type ‖ " " ‖ name.

Example:

Mail(address from,address to,string contents)

Data Encoding

Definition of hash_struct

The hash_struct function is defined as:

hash_struct(s : 𝕊) = keccak256(type_hash ‖ encode_data(s)) where:

  • type_hash = keccak256(encode_type(type of s))
  • ‖ represents byte concatenation
  • encode_type and encode_data are defined below

Definition of encode_data

The encoding of a struct instance is enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ), the concatenation of the encoded member values in the order they appear in the type. Each encoded member value is exactly 32 bytes long.

The values are encoded as follows:

Atomic Values:

  • Boolean false and true are encoded as u64 values 0 and 1, padded to 32 bytes
  • Address, ContractId, Identity, and b256 are encoded directly as 32 bytes
  • Unsigned Integer values (u8 to u256) are encoded as big-endian bytes, padded to 32 bytes

Dynamic Types:

  • Bytes and String are encoded as their Keccak256 hash

Reference Types:

  • Arrays (both fixed and dynamic) are encoded as the Keccak256 hash of their concatenated encodings
  • Struct values are encoded recursively as hash_struct(value)

The implementation of TypedDataHash for 𝕊 SHALL utilize the DataEncoder for encoding each element of the struct based on its type.

Final Message Encoding

The encoding of structured data follows this pattern:

encode(domain_separator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" ‖ domain_separatorhash_struct(message)

where:

  • \x19\x01 is a constant prefix
  • ‖ represents byte concatenation
  • domain_separator is the 32-byte hash of the domain parameters
  • hash_struct(message) is the 32-byte hash of the structured data

Example implementation

const MAIL_TYPE_HASH: b256 = 0x536e54c54e6699204b424f41f6dea846ee38ac369afec3e7c141d2c92c65e67f;

impl TypedDataHash for Mail {

    fn type_hash() -> b256 {
        MAIL_TYPE_HASH
    }

    fn struct_hash(self) -> b256 {
        let mut encoded = Bytes::new();
        encoded.append(
            MAIL_TYPE_HASH.to_be_bytes()
        );
        encoded.append(
            DataEncoder::encode_address(self.from).to_be_bytes()
        );
        encoded.append(
            DataEncoder::encode_address(self.to).to_be_bytes()
        );
        encoded.append(
            DataEncoder::encode_string(self.contents).to_be_bytes()
        );

        keccak256(encoded)
    }
}

Rationale

  • Domain separators provides protocol-specific context to prevent signature replay across different protocols and chains.
  • Type hashes ensure type safety and prevent collisions between different data structures
  • The encoding scheme is designed to be deterministic and injective
  • The standard maintains compatibility with existing Sway types and practices

Backwards Compatibility

This standard is compatible with existing Sway data structures and can be implemented alongside other Fuel standards. It does not conflict with existing signature verification methods.

Type System Compatibility Notes

When implementing SRC16 in relation to EIP712, the following type mappings and considerations apply:

String Encoding

  • Both standards use the same String type and encoding
  • SRC16 specifically uses String type only (not Sway's str or str[])
  • String values are encoded identically in both standards using keccak256 hash

Fixed Bytes

  • EIP712's bytes32 maps directly to Sway's b256
  • Encoded using encode_b256 in the DataEncoder
  • Both standards handle 32-byte values identically
  • Smaller fixed byte arrays (bytes1 to bytes31) are not supported in SRC16

Address Types

  • EIP712 uses 20-byte Ethereum addresses
  • When encoding an EIP712 address, SRC16:
    • Takes only rightmost 20 bytes from a 32-byte Fuel Address
    • Pads with zeros on the left for EIP712 compatibility
    • Example: Fuel Address of 32 bytes becomes rightmost 20 bytes in EIP712 encoding

ContractId Handling

  • ContractId is unique to Fuel/SRC16 (no equivalent in EIP712)
  • When encoding for EIP712 compatibility:
    • Uses rightmost 20 bytes of ContractId
    • Particularly important in domain separators where EIP712 expects a 20-byte address

Domain Separator Compatibility

// SRC16 Domain (Fuel native)
pub struct SRC16Domain {
    name: String,                   // Same as EIP712
    version: String,                // Same as EIP712
    chain_id: u64,                  // Fuel chain ID
    verifying_contract: ContractId, // Full 32-byte ContractId
}

// EIP712 Domain (Ethereum compatible)
pub struct EIP712Domain {
    name: String,
    version: String,
    chain_id: u256,
    verifying_contract: b256,      // Only rightmost 20 bytes used
}

Note on verifying_contract field; When implementing EIP712 compatibility within SRC16, the verifying_contract address in the EIP712Domain must be constructed by taking only the rightmost 20 bytes from either a Fuel ContractId. This ensures proper compatibility with Ethereum's 20-byte addressing scheme in the domain separator.

// Example ContractId conversion:
// Fuel ContractId (32 bytes):
//   0x000000000000000000000000a2233d3bf2aa3f0cbbe824eb04afc1acc84c364c
//                            └─────────────── 20 bytes ───────────────┘
//
// EIP712 Address (20 bytes):
//   0xa2233d3bf2aa3f0cbbe824eb04afc1acc84c364c
//    └─────────────── 20 bytes ───────────────┘

Note on EIP712 Domain Separator salt; Within EIP712 the field salt is an optional field to be used at the discretion of the protocol designer. Within SRC16 the EIP712Domain does not use the salt field. The other fields in EIP712Domain are mandatory within SRC16.

Security Considerations

Replay Attacks

Implementations must ensure signatures cannot be replayed across:

Different chains (prevented by chain_id) Different protocols (prevented by domain separator) Different contracts (prevented by verifying_contract)

Type Safety

Implementations must validate all type information and enforce strict encoding rules to prevent type confusion attacks.

Example Implementation

Example of the SRC16 implementation where a contract utilizes the encoding scheme to produce a typed structured data hash of the Mail type.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src16-typed-data/fuel_example/src/main.sw' -->
<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src16-typed-data/ethereum_example/src/main.sw' -->

SRC-20: Native Asset

The following standard allows for the implementation of a standard API for Native Assets using the Sway Language. This standard provides basic functionality as well as on-chain metadata for other applications to use.

Motivation

A standard interface for Native Assets on Fuel allows external applications to interact with the native asset, whether that be decentralized exchanges, wallets, or Fuel's Scripts and Predicates.

Prior Art

The SRC-20 Native Asset Standard naming pays homage to the ERC-20 Token Standard seen on Ethereum. While there is functionality we may use as a reference, it is noted that Fuel's Native Assets are fundamentally different than Ethereum's tokens.

There has been a discussion of the Fungible Token Standard on the Fuel Forum. This discussion can be found here.

There has also been a Fungible Token Standard and Non-Fungible Token Standard implementations added to the Sway-Libs repository before the creation of the Sway-Standards repository. The introduction of this standard in the Sway-Standards repository will deprecate the Sway-Libs Fungible Token Standard.

Specification

Required Public Functions

The following functions MUST be implemented to follow the SRC-20 standard:

fn total_assets() -> u64

This function MUST return the total number of individual assets for a contract.

fn total_supply(asset: AssetId) -> Option<u64>

This function MUST return the total supply of coins for an asset. This function MUST return Some for any assets minted by the contract.

fn name(asset: AssetId) -> Option<String>

This function MUST return the name of the asset, such as “Ether”. This function MUST return Some for any assets minted by the contract.

fn symbol(asset: AssetId) -> Option<String>

This function must return the symbol of the asset, such as “ETH”. This function MUST return Some for any assets minted by the contract.

fn decimals(asset: AssetId) -> Option<u8>

This function must return the number of decimals the asset uses - e.g. 8, which means to divide the coin amount by 100000000 to get its user representation. This function MUST return Some for any assets minted by the contract.

Non-Fungible Asset Restrictions

Non-Fungible Tokens (NFT) or Non-Fungible Assets on Fuel are Native Assets and thus follow the same standard as Fungible Native Assets with some restrictions. For a Native Asset on Fuel to be deemed an NFT, the following must be applied:

  • Non-Fungible Assets SHALL have a total supply of one per asset.
  • Non-Fungible Assets SHALL have a decimal of 0u8.

Logging

The following logs MUST be implemented and emitted to follow the SRC-20 standard.

  • IF a value is updated via a function call, a log MUST be emitted.
  • IF a value is embedded in a contract as a constant, configurable, or other manner, an event MUST be emitted at least once.

SetNameEvent

The SetNameEvent MUST be emitted when the name of an asset has updated.

There SHALL be the following fields in the SetNameEvent struct:

  • asset: The asset field SHALL be used for the corresponding AssetId of the asset has been updated.
  • name: The name field SHALL be used for the corresponding Option<String> which represents the name of the asset.
  • sender: The sender field SHALL be used for the corresponding Identity which made the function call that has updated the name of the asset.

Example:

pub struct SetNameEvent {
    pub asset: AssetId,
    pub name: Option<String>,
    pub sender: Identity,
}

SetSymbolEvent

The SetSymbolEvent MUST be emitted when the symbol of an asset has updated.

There SHALL be the following fields in the SetSymbolEvent struct:

  • asset: The asset field SHALL be used for the corresponding AssetId of the asset has been updated.
  • symbol: The symbol field SHALL be used for the corresponding Option<String> which represents the symbol of the asset.
  • sender: The sender field SHALL be used for the corresponding Identity which made the function call that has updated the symbol of the asset.

Example:

pub struct SetSymbolEvent {
    pub asset: AssetId,
    pub symbol: Option<String>,
    pub sender: Identity,
}

SetDecimalsEvent

The SetDecimalsEvent MUST be emitted when the decimals of an asset has updated.

There SHALL be the following fields in the SetDecimalsEvent struct:

  • asset: The asset field SHALL be used for the corresponding AssetId of the asset has been updated.
  • decimals: The decimals field SHALL be used for the corresponding u8 which represents the decimals of the asset.
  • sender: The sender field SHALL be used for the corresponding Identity which made the function call that has updated the decimals of the asset.

Example:

pub struct SetDecimalsEvent {
    pub asset: AssetId,
    pub decimals: u8,
    pub sender: Identity,
}

UpdateTotalSupplyEvent

The UpdateTotalSupplyEvent MUST be emitted when the total supply of an asset has updated.

There SHALL be the following fields in the UpdateTotalSupplyEvent struct:

  • asset: The asset field SHALL be used for the corresponding AssetId of the asset has been updated.
  • supply: The supply field SHALL be used for the corresponding u64 which represents the total supply of the asset.
  • sender: The sender field SHALL be used for the corresponding Identity which made the function call that has updated the total supply of the asset.

Example:

pub struct UpdateTotalSupplyEvent {
    pub asset: AssetId,
    pub supply: u64,
    pub sender: Identity,
}

Rationale

As the SRC-20 Native Asset Standard leverages Native Assets on Fuel, we do not require the implementation of certain functions such as transfer or approval. This is done directly within the FuelVM and there is no smart contract that requires updating of balances. As Fuel is UTXO based, any transfer events may be indexed on transaction receipts.

Following this, we have omitted the inclusion of any transfer functions or events. The provided specification outlines only the functions necessary to implement fully functional native assets on the Fuel Network. Additional functionality and properties may be added as needed.

Backwards Compatibility

This standard is compatible with Fuel's Native Assets. There are no other standards that require compatibility.

Security Considerations

This standard does not introduce any security concerns, as it does not call external contracts, nor does it define any mutations of the contract state.

Example ABI

abi SRC20 {
    #[storage(read)]
    fn total_assets() -> u64;
    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64>;
    #[storage(read)]
    fn name(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8>;
}

Example Implementation

Single Native Asset

Example of the SRC-20 implementation where a contract contains a single asset with one SubId. This implementation is recommended for users that intend to deploy a single asset with their contract.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src20-native-asset/multi_asset/src/multi_asset.sw' -->

Multi Native Asset

Example of the SRC-20 implementation where a contract contains multiple assets with differing SubIds. This implementation is recommended for users that intend to deploy multiple assets with their contract.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src20-native-asset/single_asset/src/single_asset.sw' -->

Solidity

A quick SoliditySway cross reference for the most commonly used items

  • block.timestamp
  • msg.sender
  • etc

If something is missing here you can most likely find it in the Sway STD Library

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/solidity_cheatsheet/src/main.sw' -->

Hello Sway

The contract keyword at the top defines one of the four program types found in Sway. Others being libraries, scripts and predicates.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/hello_sway/src/main.sw' -->

Variables

Examples of variables in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/variables/src/main.sw' -->

Primitive Types

Examples of primitive data types in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/primitive_types/src/main.sw' -->

Compound Types

Examples of compound data types in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/compound_types/src/main.sw' -->

Blockchain Types

Examples of blockchain data types in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/blockchain_types/src/main.sw' -->

Functions

Examples of functions in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/functions/src/main.sw' -->

Imports

Examples of imports in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/imports/src/main.sw' -->

Project Structures

Internal


└── imports
    ├── Forc.toml
    └── src
        ├── imports_library.sw
        └── main.sw

External


├── imports
│   ├── Forc.toml
│   └── src
│       ├── imports_library.sw
│       └── main.sw
└── math_lib
    ├── Forc.toml
    └── src
        ├── Q64x64.sw
        ├── full_math.sw
        └── math_lib.sw

All external imports must be defined as dependencies within Forc.toml

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/imports/Forc.toml' -->

Structs

Examples of structs in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/structs/src/main.sw' -->

Tuples

Examples of tuples in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/tuples/src/main.sw' -->

Enums

Examples of enums in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/enums/src/main.sw' -->

Constants

Examples of constants in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/constants/src/main.sw' -->

Configurable Constants

Examples of configurable constants in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/configurable_constants/src/main.sw' -->

Options

Examples of options in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/options/src/main.sw' -->

Results

Examples of if statements in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/results/src/main.sw' -->

If Statements

Examples of if statements in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/control_flow_if/src/main.sw' -->

Match Statements

Examples of match statements in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/control_flow_match_statement/src/main.sw' -->

While Loop

Examples of while loop in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/control_flow_while_loop/src/main.sw' -->

Logging

Examples of logging in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/logging/src/main.sw' -->

Storage Map

Examples of storage map in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/storage_map/src/main.sw' -->

Vector

Examples of vectors in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/vector/src/main.sw' -->

Base Asset

Examples of base asset in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/base_asset/src/main.sw' -->

Library

Example on how to create a library in Sway and how to use it in your Smart Contract. This example also showcases how to use different types of imports in Sway depending on external library or library from the same project.

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/library/src/main.sw' -->

Predicate

Examples of a predicate program type in Sway

PredicatesContracts
Access data on chain
Read data from smart contracts
Check date or time
Read block hash or number
Read input coins
Read output coins
Read transaction scripts
Read transaction bytecode
<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/predicate/src/main.sw' -->

Script

Examples of a script program type in Sway

PredicatesScripts
Access data on chain
Read data from smart contracts
Check date or time
Read block hash or number
Read input coins
Read output coins
Read transaction scripts
Read transaction bytecode
<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/script/src/main.sw' -->

Big Numbers

Examples of Big Numbers in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/big_number/src/main.sw' -->

SRC20

Examples of a SRC 20 contract in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/src20/src/main.sw' -->

Hashing with Keccak256

Example of how to compute hash in Sway using Keccak256

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/hashing/src/main.sw' -->

Verifying Signatures in Sway

Example of how to verify signatures in Sway

<!-- MDBOOK-INCLUDE-ERROR: File not found '../examples/verify_signature/src/main.sw' -->

Migrations

This section provides information to help with breaking changes, but for full releases, please reference the GitHub release notes tagged in the respective modules.

Sway

Full migration reference can be found here

Rust SDK

Full migration reference can be found here

Typescript SDK

Full migration reference can be found here

Sway Migrations Guide

March 13, 2024

Release v0.67.0

New forc migrate

Below is a simplified example of how to migrate your project quickly. For more information on how to use forc migrate please refer to the forc migrate docs.

Important: Using forc migrate requires you to use the version of Sway right before the breaking change version.

For example, breaking changes for Sway will come in version v0.67.0, you will need to use v0.66.10 to run forc migrate, in order to migrate properly.

You can compile and migrate using the previous latest version by running the following command:

fuelup component add forc@0.66.10

1. Run forc migrate show

Running forc migrate show will inform you about all the breaking changes in the next release. For example:

Breaking change features:
  - storage_domains            (https://github.com/FuelLabs/sway/issues/6701)
  - partial_eq                 (https://github.com/FuelLabs/sway/issues/6883)
  - try_from_bytes_for_b256    (https://github.com/FuelLabs/sway/issues/6994)
  - merge_core_std             (https://github.com/FuelLabs/sway/issues/7006)

Migration steps (1 manual, 3 semiautomatic, and 3 automatic):
storage_domains
  [M] Review explicitly defined slot keys in storage declarations (`in` keywords)
  [S] Explicitly define storage slot keys if they need to be backward compatible

partial_eq
  [A] Implement experimental `PartialEq` and `Eq` traits
  [S] Remove deprecated `Eq` trait implementations and `experimental_partial_eq` attributes

try_from_bytes_for_b256
  [A] Replace `b256::from(<bytes>)` calls with `b256::try_from(<bytes>).unwrap()`
  [A] Replace `<bytes>.into()` calls with `<bytes>.try_into().unwrap()`

merge_core_std
  [S] Replace `core` with `std` in paths

Experimental feature flags:
- for Forc.toml:  experimental = { storage_domains = true, partial_eq = true, try_from_bytes_for_b256 = true, merge_core_std = true }
- for CLI:        --experimental storage_domains,partial_eq,try_from_bytes_for_b256,merge_core_std

2. Update forc.toml dependencies

In your Sway project, update the forc.toml file to use the previous latest version of Sway.

// before

[dependencies]
standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.6.1" }
sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.24.0" }
// after

[dependencies]
standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.6.3" }
sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.24.2" }

3. Run forc migrate run

Running forc migrate run walks you through each of the breaking changes and helps you apply them to your project. If you just want to see the breaking changes in your project without migrating them, you can run forc migrate check.

   Compiling mira_amm_contract (/mira-v1-core/contracts/mira_amm_contract)
warning: unused manifest key: build-profile.?.release.experimental
   Migrating Breaking change feature storage_domains
     Checked [storage_domains]  Review explicitly defined slot keys in storage declarations (`in` keywords)
      Review [storage_domains]  Explicitly define storage slot keys if they need to be backward compatible
info: [storage_domains] Explicitly define storage slot keys if they need to be backward compatible
  --> /mira-v1-core/contracts/mira_amm_contract/src/main.sw:65:1
   |
63 |   
64 |   
65 | / storage {
66 | |     /// Pools storage
...  |
79 | |     hook: Option<ContractId> = None,
80 | | }
   | |_-
   |
   = help: If the contract owning this storage is behind a proxy, or for any other reason needs
   = help: to use previous storage slot keys, those keys must be explicitly assigned to the
   = help: storage fields by using the `in` keyword.
   = help:  
   = help: E.g.:
   = help:     storage {
   = help:         field in <previous slot key>: u64 = 0,
   = help:     }
   = help:  
   = help: The previous formula for calculating storage keys was: `sha256("storage.<field name>")`.
   = help: The new formula is:                                    `sha256((0u8, "storage.<field name>"))`.
   = help:  
   = help: This migration step will interactively modify the code, based on your input.
   = help:  
   = help: For a detailed migration guide see: https://github.com/FuelLabs/sway/issues/6701
____

The following storage fields will have slot keys calculated by using the new formula:
  - storage.pools
  - storage.total_pools
  - storage.total_reserves
  - storage.lp_total_supply
  - storage.lp_name
  - storage.protocol_fees
  - storage.hook

Do you want these fields to have backward compatible storage slot keys, calculated
by using the previous formula?

If yes, this migration step will insert `in` keywords to all of the above fields,
and calculate the storage slot keys by using the previous formula.

1. Yes, assign the backward compatible storage slot keys.
2. No, this contract does not require backwards compatibility.
Enter your choice [1..2]: 1
    Changing [storage_domains]  Explicitly define storage slot keys if they need to be backward compatible
Source code successfully changed (7 changes).
    Finished Project is compatible with the next breaking change version of Sway

4. Switch to the latest version of Sway

// Assuming you have 0.67.0 installed
fuelup default latest

5. Compile your project

forc build

Using the forc migrate tool is highly recommended, and the changes below are only for reference.

Compiler/std-lib: storage collison between variables and StorageMap, allows hidden backdoors, likely loss of funds - #6701

Certain storage types, like, e.g., StorageMap allow storage slots of their contained elements to be defined based on developer's input. E.g., the key in a StorageMap used to calculate the storage slot is a developer input.

To ensure that pre-images of such storage slots can never be the same as a pre-image of compiler generated key of a storage field, we will prefix the pre-images of storage fields with a single byte that denotes the storage field domain. Storage types like StorageMap must have a different domain prefix than this storage field domain which will be set to 0u8.

// before
sha256("storage::<optional namespace 1>::<optional namespace 2>.<field name>")
// after
sha256((0u8, "storage::<optional namespace 1>::<optional namespace 2>.<field name>"))

If the contract owning the storage is behind a proxy, its storage field keys must be backward compatible and the same as the old keys. In this, and any other case where the backward compatibility of the storage slot keys is needed, the old keys must be explicitly defined for storage fields, by using the in keyword and the old keys.

E.g., assume we have a contract with the following storage behind a proxy:

// before
storage {
    x: u64 = 0,
    namespace_1 {
        y: u64 = 0,
        namespace_2 {
            z: u64 = 0,
        }
    }
}
// after
storage {
    x in 0xc979570128d5f52725e9a343a7f4992d8ed386d7c8cfd25f1c646c51c2ac6b4b: u64 = 0,
    namespace_1 {
        y in 0x2f055029200cd7fa6751421635c722fcda6ed2261de0f1e0d19d7f257e760589: u64 = 0,
        namespace_2 {
            z in 0x03d2ee7fb8f3f5e1084e86b02d9d742ede96559e44875c6210c7008e2d184694: u64 = 0,
        }
    }
}

Replace Eq trait implementations with PartialEq and Eq implementations - #6883

Partial equivalence feature renames the current Eq trait to PartialEq and adds a new, empty Eq trait with PartialEq as a supertrait.

Every existing Eq trait implementation needs to be renamed to PartialEq, and in addition, an empty Eq implementations needs to be added.

// before
impl Eq for SomeType {
    fn eq(self, other: Self) -> bool {
        self.x == other.x
    }
}
// after
impl PartialEq for SomeType {
    fn eq(self, other: Self) -> bool {
        self.x == other.x
    }
}

impl Eq for SomeType {}

If the original implementation implements Eq for a generic type and in addition has Eq on trait constraints, those Eq trait constraints must be replaced by PartialEq in the new PartialEq impl, and remain Eq in the new, empty, Eq impl.

// before
impl<A, B> Eq for (A, B)
where
    A: Eq,
    B: Eq,
{
    fn eq(self, other: Self) -> bool {
        self.0 == other.0 && self.1 == other.1
    }
}
// after
impl<A, B> PartialEq for (A, B)
where
    A: PartialEq,
    B: PartialEq,
{
    fn eq(self, other: Self) -> bool {
        self.0 == other.0 && self.1 == other.1
    }
}

impl<A, B> Eq for (A, B)
where
    A: Eq,
    B: Eq,
{}

Implement TryFrom<Bytes> for b256 - #6994

Replace calls to from(bytes)/bytes.into() with try_from(bytes)/bytes.try_into().

Calls to from:

// before
let result = b256::from(some_bytes);
// after
let result = b256::try_from(some_bytes).unwrap();

Calls to into:

// before
let result = some_bytes.into();
// after
let result = some_bytes.try_into().unwrap();

Merge core and std libraries - #7006

Currently, we have two standard libraries, core and std. The distinction between them is rather arbitrary, and we want to merge them into a single library called std. All the existing modules in the core library will be moved to the std library, but their content will not be changed.

// before
use core::ops::*;

impl core::ops::Eq for SomeType {
    fn eq(self, other: Self) -> bool {
        self.x == other.x
    }
}

let _ = core::codec::encode(0u64);
// after
use std::ops::*;

impl std::ops::Eq for SomeType {
    fn eq(self, other: Self) -> bool {
        self.x == other.x
    }
}

let _ = std::codec::encode(0u64);

August 16, 2024

Release v0.63.0

#[namespace()] attribute is no longer supported - #6279

We no longer support the #[namespace()] attribute. If you use it, notably with SRC14, you should migrate to using the in keyword if you want backwards compatibility. If you just care about namespacing, you should use the new namespacing syntax.

Backwards compatibility places foo at sha256("storage_example_namespace_0")

// before
#[namespace(example_namespace)]
storage {
    foo: u64 = 0,
}
// after
storage {
    foo in 0x1102bf23d7c2114d6b409df4a1f8f7982eda775e800267be65c1cc2a93cb6c5c: u64 = 0,
}

New / recommended method places foo at sha256("storage::example_namespace.foo")

// new
storage {
    example_namespace {
        foo: u64 = 0,
    },
}

Configurables are no longer allowed in pattern matching and shadowing - #6289

The code below does not compile any more.

configurable {
    X: u8 = 10,
}

fn main() {
    let X = 101u8; // ERROR: Variable "X" shadows configurable of the same name.
}
configurable {
    X: u8 = 10,
}

fn main() {
    match var {
        (0, X) => true // ERROR: "X" is a configurable and configurables cannot be matched against.
    }
}

New ABI specification format - #6254

The new ABI specification format is hash based to improve support for indexing. There were also updates to support the latest VM features.

Added variable length message support when verifying ed signatures - #6419

ed_verify was changed to use Bytes for the message instead of b256 for a message hash.

// before
pub fn ed_verify(public_key: b256, signature: B512, msg_hash: b256)
// after
pub fn ed_verify(public_key: b256, signature: B512, msg: Bytes)

Some STD functions now return an Option instead of reverting - #6405, #6414, #6418

Some functions in the STD now return an Option instead of reverting. This allows developers to fail gracefully. More functions will do this in the future.

// before
let my_predicate_address: Address = predicate_address();
// after
let my_predicate_address: Address = predicate_address().unwrap();

Some STD functions now return types have been updated to match the Fuel Specifications

  • output_count() now returns a u16 over a u64

Before:

let output_count: u64 = output_count();

After:

let my_output_count: u16 = output_count();
  • tx_maturity now returns an Option<u32> over an Option<u64>

Before:

let my_tx_maturity: u64 = tx_maturity().unwrap()

After:

let my_tx_maturity: u32 = tx_maturity().unwrap()

Some STD functions have been made private. These will no longer be available for developers to use

  • input_pointer()
  • output_pointer()
  • tx_witness_pointer()
  • tx_script_start_pointer()
  • tx_script_data_start_pointer()

The following functions now follow this format:

Inputs:

  • input_type()
  • input_predicate_data()
  • input_predicate()
  • input_message_sender()
  • input_message_recipient()
  • input_message_data_length()
  • input_message_data()
  • input_message_nonce()

Outputs:

  • output_type()
  • output_amount()

Transactions:

  • tx_script_length()
  • tx_script_data_length()
  • tx_witness_data_length()
  • tx_witness_data()
  • tx_script_data()
  • tx_script_bytecode()
  • tx_script_bytecode_hash()

Non-breaking Changes

New partial support for slices.

Automated proxy creation and deployment with forc.

Rust SDK Migrations Guide

March 17, 2025

Release v0.71.0

Bump minimum fuel-core-* versions - #1600

Minimum fuel-core-* versions bumped to 0.41.7

#![allow(unused)]
fn main() {
// before
fuel-core = { version = "0.41.3", default-features = false, features = [
  "wasm-executor",
] }
fuel-core-chain-config = { version = "0.41.3", default-features = false }
...
}
#![allow(unused)]
fn main() {
// after
fuel-core = { version = "0.41.7", default-features = false, features = [
  "wasm-executor",
] }
fuel-core-chain-config = { version = "0.41.7", default-features = false }
...
}

Wallet refactoring - #1620

ImpersonatedAccount is removed

To achieve the same functionality instantiate a `FakeSigner:

#![allow(unused)]
fn main() {
// before
let address =
    Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
        .unwrap();
let address = Bech32Address::from(address);
let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));
}
#![allow(unused)]
fn main() {
// after
let some_address = wallet.address().clone();
let fake_signer = FakeSigner::new(some_address);
let impersonator = Wallet::new(fake_signer, provider.clone());
}

AwsKmsSigner and GoogleKmsSigner moved

under fuels::accounts::signers::kms::aws and fuels::accounts::signers::kms::google, respectfully.

#![allow(unused)]
fn main() {
// before
use fuels::accounts::kms::AwsKmsSigner;
use fuels::accounts::kms::GoogleKmsSigner;
}
#![allow(unused)]
fn main() {
// after
use fuels::accounts::signers::kms::aws::AwsKmsSigner;
use fuels::accounts::signers::kms::google::GoogleKmsSigner;
}

KmsWallet removed

use an ordinary Wallet now with a kms signer (aws or google)

WalletUnlocked and Wallet substituted by Wallet<Unlocked<S = PrivateKeySigner>> or Wallet<Locked>

#![allow(unused)]
fn main() {
// before
wallet.set_provider(provider.clone());

...

let mut wallet = WalletUnlocked::new_random(None);

let coins: Vec<Coin> = setup_single_asset_coins(
    wallet.address(),
    Default::default(),
    DEFAULT_NUM_COINS,
    DEFAULT_COIN_AMOUNT,
);

let chain_config = ChainConfig {
    consensus_parameters: consensus_parameters.clone(),
    ..ChainConfig::default()
};

let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
wallet.set_provider(provider.clone());
assert_eq!(consensus_parameters, provider.consensus_parameters().await?);

...

let wallet = WalletUnlocked::new_random(None);
}
#![allow(unused)]
fn main() {
// after
let wallet = Wallet::new(signer, provider.clone());

...

let mut rng = thread_rng();
let signer = PrivateKeySigner::random(&mut rng);

let coins: Vec<Coin> = setup_single_asset_coins(
    signer.address(),
    Default::default(),
    DEFAULT_NUM_COINS,
    DEFAULT_COIN_AMOUNT,
);
let chain_config = ChainConfig {
    consensus_parameters: consensus_parameters.clone(),
    ..ChainConfig::default()
};

let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
let wallet = Wallet::new(signer, provider.clone());
assert_eq!(consensus_parameters, provider.consensus_parameters().await?);

...

let wallet = launch_provider_and_get_wallet().await?;
}

The provider is now mandatory for Wallet::new.

Common operations in the new API:

Creating a random wallet

a) Two step (useful when you haven't started the node but need the address)

#![allow(unused)]
fn main() {
    // Create a random private key signer
    let signer = PrivateKeySigner::random(&mut rng);
    let coins = setup_single_asset_coins(signer.address(), asset_id, 1, DEFAULT_COIN_AMOUNT);
    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    let wallet = Wallet::new(signer, provider);
}

b) One step (when you already have a provider)

#![allow(unused)]
fn main() {
    let wallet = Wallet::random(&mut rng, provider.clone());
}
Locking a wallet
#![allow(unused)]
fn main() {
    let locked_wallet = wallet.lock();
}
Creating a locked wallet
#![allow(unused)]
fn main() {
    let wallet = Wallet::new_locked(addr, provider.clone());
}
Wallets no longer sign

You use one of the signers for that. Or, if your wallet is unlocked, get its signer by calling wallet.signer().

ViewOnlyAccount no longer requires core::fmt::Debug and core::clone::Clone as supertraits

Wallet no longer handles encrypting keys for disk storage

Use the fuels::accounts::Keystore for that (feature-gated under accounts-keystore)

AWS/Google kms feature flags changed

They're now accounts-signer-aws-kms and accounts-signer-google-kms.

Use total_gas and total_fee from tx status - #1574

  • Removed get_response_from method from CallHandlers
  • CallResponse refactored and added tx_status: Success field
  • Method get_response accepts TxStatus instead of Vec<Receipts>
  • Method new is removed form CallResponse
  • GasValidation trait is removed from transaction builders
  • Accounts transfer method returns Result<TxResponse>
  • Accounts force_transfer_to_contract method returns Result<TxResponse>
  • Accounts withdraw_to_base_layer method returns Result<WithdrawToBaseResponse>
  • Executable<Loader>'s upload_blob returns Result<Option<TxResponse>>
  • Contract's deploy and deploy_if_not_exists return Result<DeployResponse> and Response<Option<DeployResponse>> respectively
  • TransactionCost's field gas_used renamed to script_gas

August 16, 2024

Release v0.66.0

Unfunded read only calls - #1412

SizedAsciiString no longer implements AsRef<[u8]>. To get the underlying bytes you can turn it into a &str via the new AsRef<str> and call as_bytes() on the &str: `sized_string.as_ref().as_bytes()``

#![allow(unused)]
fn main() {
// before
let bytes: &[u8] = sized_str.as_ref();
}
#![allow(unused)]
fn main() {
// after
let bytes: &[u8] = sized_str.as_ref().as_bytes();
}

build_without_signatures is now achieved by setting the build strategy to BuildStrategy::NoSignatures on the transaction builder before calling build

#![allow(unused)]
fn main() {
// before
let mut tx = tb.build_without_signatures(provider).await?;
}
#![allow(unused)]
fn main() {
// after
let mut tx = tb.with_build_strategy(ScriptBuildStrategy::NoSignatures).build(provider).await?;
}

.simulate() now accepts an Execution argument allowing for Realistic or StateReadOnly simulations.

#![allow(unused)]
fn main() {
// before
let stored = contract_methods.read().simulate().await?;
}
#![allow(unused)]
fn main() {
// after
let stored = contract_methods.read().simulate(Execution::StateReadOnly).await?;
}

Accounts now cover max fee increase due to tolerance - #1464

fee_checked_from_tx is removed from all transaction builders. max fee can now be estimated using the new method estimate_max_fee which takes into account the max fee estimation tolerance set on the builders.

#![allow(unused)]
fn main() {
// before
let transaction_fee = tb.fee_checked_from_tx(provider)
    .await?
    .ok_or(error_transaction!(
        Other,
        "error calculating `TransactionFee`"
    ))?;

let total_used = transaction_fee.max_fee() + reserved_base_amount;
}
#![allow(unused)]
fn main() {
// after
let max_fee = tb.estimate_max_fee(provider).await?;

let total_used = max_fee + reserved_base_amount;
}

Account impersonation - #1473

The SDK previously performed transaction validity checks, including signature verification, before sending a transaction to the network. This was problematic since the checks also included signature verification even when utxo validation was turned off. To enable this feature and prevent future issues like failed validation checks due to version mismatches between the network and the SDK's upstream dependencies, we decided to remove the check. Since the SDK already abstracts building transactions for common cases (contract calls, transfers, etc.), validity issues are unlikely. If needed, we can still expose the validity checks as part of the transaction builder or our transaction structs.

#![allow(unused)]
fn main() {
/*

A `ImpersonatedAccount` simulates ownership of assets held by an account with a given address.
`ImpersonatedAccount` will only succeed in unlocking assets if the the network is setup with
utxo_validation set to false.

*/

let node_config = NodeConfig {
    utxo_validation: false,
    ..Default::default()
};
}

Deploying large contracts (loader + blob support) - #1472

Contract::new is removed, replaced with Contract::regular with three states

First: A regular contract

What you're used to seeing. It is either initialized from raw code or loaded from a file:

#![allow(unused)]
fn main() {
let contract = Contract::regular(contract_binary, Salt::zeroed(), vec![]);
}

or

#![allow(unused)]
fn main() {
let contract = Contract::load_from(
    "sway/contracts/storage/out/release/storage.bin",
    LoadConfiguration::default(),
)?;
}

With the notable addition of being able to set configurables (previously possible only when using load_from):

#![allow(unused)]
fn main() {
let contract = Contract::regular(binary, Salt::zeroed(), vec![]).with_configurables(configurables);
}

a regular contract can be deployed via deploy, which hasn't changed, or via smart_deploy that will use blobs/loader if the contract is above what can be deployed in a create tx:

#![allow(unused)]
fn main() {
let contract_id = Contract::load_from(
    contract_binary,
    LoadConfiguration::default().with_salt(random_salt()),
)?
.smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
.await?;
}

Second: Loader contract, blobs pending upload

You can turn a regular contract into a loader contract:

#![allow(unused)]
fn main() {
let contract = Contract::load_from(
    contract_binary,
    LoadConfiguration::default(),
)?
.convert_to_loader(max_words_per_blob)?
}

or, if you have the blobs, create it directly:

#![allow(unused)]
fn main() {
let contract = Contract::loader_for_blobs(blobs, random_salt(), vec![])?;
}

You can also revert back to the regular contract via revert_to_regular.

If you now call deploy the contract will first deploy the blobs and then the loader itself.

You can also split this into two parts by first calling upload_blobs and then deploy:

#![allow(unused)]
fn main() {
let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
    .convert_to_loader(1024)?
    .upload_blobs(&wallet, TxPolicies::default())
    .await?
    .deploy(&wallet, TxPolicies::default())
    .await?;
}

doing so will have deploy only submit the create tx while the uploading will be done in upload_blobs.

Third: Loader, with blobs deployed

You arrive at this contract type by either having the blob ids and creating it manually:

#![allow(unused)]
fn main() {
let contract = Contract::loader_for_blob_ids(all_blob_ids, random_salt(), vec![])?;
}

or by calling upload_blobs as in the previous case:

#![allow(unused)]
fn main() {
let contract = Contract::load_from(
    contract_binary,
    LoadConfiguration::default().with_salt(random_salt()),
)?
.convert_to_loader(max_words_per_blob)?
.upload_blobs(&wallet, TxPolicies::default())
.await?;
}

Calling deploy on this contract only deploys the loader.

TypeScript SDK Migrations Guide

March 17, 2025

Release v0.100.0

Made ResourceCache consider resource owner - #3697

//before
provider.cache?.getActiveData();
provider.cache?.isCached(key);
//after
const owner = wallet.address.toB256();

provider.cache?.getActiveData(owner)
provider.cache?.isCached(owner, key);

Upgrade fuel-core to 0.41.7 - #3590

Because of the latest fuel-core changes, TS SDK does not throw the following error codes and messages anymore:

1. NOT_ENOUGH_FUNDS

// before
"The account(s) sending the transaction don't have enough funds to cover the transaction."
// after
"Insufficient funds or too many small value coins. Consider combining UTXOs."

2. MAX_COINS_REACHED

// before
"The account retrieving coins has exceeded the maximum number of coins per asset. Please consider combining your coins into a single UTXO."
// after
"Insufficient funds or too many small value coins. Consider combining UTXOs."

Both error codes were removed in favor of INSUFFICIENT_FUNDS_OR_MAX_COINS

February 4, 2025

Release v0.99.0

Remove pageInfo from getBalances GraphQL operations - #3652

  • The pageInfo field has been removed from the response of the provider.operations.getBalances query.
// before
const { balances, pageInfo } = await provider.operations.getBalances({
  first: 100,
  filter: { owner: wallet.address.toB256() },
});
// after
const { balances } = await provider.operations.getBalances({
  first: 100,
  filter: { owner: wallet.address.toB256() },
});

The getBalances method of the Provider class remains unchanged, as it never returned pageInfo:

// not affected
const { balances } = await provider.getBalances();

Remove ContractUtils namespaced export - #3570

  • ContractUtils was removed and the underlying functions getContractRoot(), getContractStorageRoot(), getContractId(), hexlifyWithPrefix() are now exported directly from fuels.
// before
import { ContractUtils } from 'fuels';
// after
import { getContractRoot, getContractStorageRoot, getContractId, hexlifyWithPrefix } from 'fuels';

January 10, 2025

Release v0.98.0

Making provider initialization sync again - #3514

1. Provider Instantiation

  • Going from async to sync
// before
const provider = await Provider.create(NETWORK_URL);
// after
const provider = new Provider(NETWORK_URL);

2. Provider methods

  • The following methods are now async
// before
provider.getNode();
provider.getChain();
provider.getChainId();
provider.getBaseAssetId();
provider.getGasConfig();
provider.validateTransaction();
// after
await provider.getNode();
await provider.getChain();
await provider.getChainId();
await provider.getBaseAssetId();
await provider.getGasConfig();
await provider.validateTransaction();

3. TransferParams and ContractTransferParams

export type TransferParams = {
  destination: string | AbstractAddress;
  amount: BigNumberish;
-  assetId?: BytesLike;
+  assetId: BytesLike;
};

export type ContractTransferParams = {
  contractId: string | AbstractAddress;
  amount: BigNumberish;
-  assetId?: BytesLike;
+  assetId: BytesLike;
};

4. Transaction Response

  • The constructor now requires a chainId
// before
new TransactionResponse('0x..', provider);
// after
new TransactionResponse('0x..', provider, chainId);

autoCost for transaction estimation and funding - #3539

To be brought inline with autoCost, funding a contract and script call has been migrated from fundWithRequiredCoins to autoCost:

// before
const request: ScriptTransactionRequest = contract.functions.add(1).fundWithRequiredCoins();
// after
const request: ScriptTransactionRequest = contract.functions.add(1).autoCost();

Remove redundant gas price call for tx summary - #3559

  • calculateTXFeeForSummary and subsequently the CalculateTXFeeForSummaryParams no longer accept a totalFee property. If you have the totalFee, then there is no need to call the calculateTxFeeForSummary() function.
// before
const totalFee = bn(..):
calculateTXFeeForSummary({ ..., totalFee } as CalculateTXFeeForSummaryParams);
// after
calculateTXFeeForSummary({ ... } as CalculateTXFeeForSummaryParams);

Prevent implicit asset burn - #3540

// before
const transactionRequest = new ScriptTransactionRequest();
transactionRequest.inputs.push({ ... });

// since outputs weren't added, assets would be burned
await sender.sendTransaction(transactionRequest);
// after
const transactionRequest = new ScriptTransactionRequest();
transactionRequest.inputs.push({ ... });

// now, an error will be thrown unless `enableAssetBurn`is true,
// in which case, assets can still be burned
await sender.sendTransaction(transactionRequest, {
  enableAssetBurn: true,
});

Remove unused operations - #3553

The following operations have been removed from the OperationName enum, as they were never used to assemble operations:

  • OperationName.mint
  • OperationName.predicatecall
  • OperationName.script
  • OperationName.sent

Remove receipts deprecated properties - #3552

All receipts deprecated properties were removed:

// before
ReceiptCall.from

ReceiptLog.val0
ReceiptLog.val1
ReceiptLog.val2
ReceiptLog.val3

ReceiptLogData.val0
ReceiptLogData.val1

ReceiptTransfer.from

ReceiptTransferOut.from
// after
ReceiptCall.id

ReceiptLog.ra
ReceiptLog.rb
ReceiptLog.rc
ReceiptLog.rd

ReceiptLogData.ra
ReceiptLogData.rb

ReceiptTransfer.id

ReceiptTransferOut.id

Remove receipt coders - #3551

All previously deprecated receipt coders have been removed. These classes were barely used aside from a few internal helpers, which were converted to utility functions.

// before
const messageId = ReceiptMessageOutCoder.getMessageId({
  sender,
  recipient,
  nonce,
  amount,
  data,
});

const assetId = ReceiptMintCoder.getAssetId(contractId, subId);

const assetId = ReceiptBurnCoder.getAssetId(contractId, subId);
// after
import { getMessageId, getAssetId } from 'fuels'

const messageId = getMessageId({
  sender,
  recipient,
  nonce,
  amount,
  data,
});

const assetId = getAssetId(contractId, subId);

Remove deprecated submitAndAwait operation - #3548

  • submitAndAwait operation was removed

After being deprecated since #3101, we have removed this operation altogether. Please use the submitAndAwaitStatus method instead which gives the same results as submitAndAwait. If you are interested in the deprecation/removal reasons, please refer to https://github.com/FuelLabs/fuel-core/issues/2108.

// before
const response = await provider.operations.submitAndAwait(txRequest);
// after
const response = await provider.operations.submitAndAwaitStatus(txRequest);

Remove Bech32 address - #3493

  • We no longer support Bech32 addresses
// before
import { Address, Bech32Address } from "fuels";

const bech32Address: Bech32Address = "fuel1234";
const address = new Address(bech32Address);
// after
import { Address, B256Address } from "fuels";

const b256Address: B256Address = "0x1234";
const address = new Address(b256Address);
  • Removed INVALID_BECH32_ADDRESS error code.

  • Removed associated Bech32 helper functions.

    • normalizeBech32
    • isBech32
    • toB256
    • getBytesFromBech32
    • toBech32
    • clearFirst12BytesFromB256

Redistributed the @fuel-ts/interfaces package - #3492

  • Removed the AbstractAddress class; use the Address class instead.
// before
import { AbstractAddress } from 'fuels';
// after
import { Address } from 'fuels';
  • Removed the @fuel-ts/interfaces package; use the fuels package instead.
// before
import { BytesLike } from '@fuel-ts/interfaces'
// after
import { BytesLike } from 'fuels'

Optimizing frontend apps - #3573

  • ScriptTransactionRequest.autoCost() has been renamed to ScriptTransactionRequest.estimateAndFund(), initially introduced by #3535
// before
await request.autoCost(wallet);
// after
await request.estimateAndFund(wallet);
  • BaseInvocationScope.autoCost() has been renamed back to BaseInvocationScope.fundWithRequiredCoins(), initially introduced by #3535
// before
const request = await contract.functions.increment().autoCost();
// after
const request = await contract.functions.increment().fundWithRequiredCoins();

November 15, 2024

Release v0.97.0

onDeploy fuels config supports all Sway program types - #3383

  • Changed the outputted data from the onDeploy callback method for the fuels.config.ts. Instead of just emitting the deployed contracts (as an array), it will now emit an object with contracts, predicates and scripts.
// Before (fuels.config.ts)
import { createConfig, FuelsConfig, DeployedContract } from 'fuels';

export default createConfig({
  output: 'dir/out',
  onDeploy: (config: FuelsConfig, deployedContracts: DeployedContract[]) => {
    console.log('contracts', deployedContracts);
  }
});
// After (fuels.config.ts)
import { createConfig, FuelsConfig, DeployedData } from 'fuels';

export default createConfig({
  output: 'dir/out',
  onDeploy: (config: FuelsConfig, deployed: DeployedData[]) => {
    console.log('contracts', deployed.contracts);
    console.log('predicates', deployed.predicates);
    console.log('scripts', deployed.scripts);
  }
});

Remove unnecessary nonce from message gql queries - #3298

  • Removed the nonce property from Provider.operations.getMessageByNonce(). This can still be retrieved by Provider.getMessageByNonce().

Refactor predicate and script deployment - #3389

ContractFactory.deployAsBlobTxForScript has been removed in favor of Predicate.deploy and Script.deploy:

// before
const factory = new ContractFactory(scriptBytecode, scriptAbi, wallet);
const { waitForResult } = await factory.deployAsBlobTxForScript();
const { loaderBytecode, configurableOffsetDiff } = await waitForResult();

// after
const script = new Script(scriptBytecode, scriptAbi, wallet);
const { blobId, waitForResult } = await script.deploy(deployerWallet);
const loaderScript = await waitForResult();

const predicate = new Predicate({ bytecode, abi, provider });
const { blobId, waitForResult } = await predicate.deploy(deployerWallet);
const loaderPredicate = await waitForResult();

Mandate abi in Predicate constructor - #3387

  • Instantiating a Predicate now requires providing its abi. If you want to use the Predicate as an Account, please instantiate it via the Account class
// before
const predicate = new Predicate({ provider, bytecode }); // worked even though abi is missing

// after
const predicate = new Predicate({ abi, provider, bytecode }); // abi is now mandatory

// predicate as account
const account = new Account(predicateAddress, provider);

Optimize getTransactions query - #3336

  • The response format for Provider.getTransactions remains the same. However, the response format for the query Provider.operations.getTransactions has been modified.
// before
query getTransactions {
  id
  rawPayload
  status {
    ...
  }
}
// after
query getTransactions {
  rawPayload
}

Limit TX pagination number for getTransactionsSummaries - #3400

  • The pagination number for getTransactionsSummaries is limited to 60 now
// before
const { transactions } = await getTransactionsSummaries({
  provider,
  filters: {
    owner: account.address.toB256(),
    first: 200,
  },
});
// after
const { transactions } = await getTransactionsSummaries({
  provider,
  filters: {
    owner: account.address.toB256(),
    first: 60, // Limit is 60 now. A higher value will result in an error
  },
});

Remove blockId in transaction list responses - #3379

  • The blockId property has been removed from the following GraphQL queries used to list past transactions:
const { transactions } = await getTransactionsSummaries({ ... });

const { transactionsByOwner } = await provider.operations.getTransactionsByOwner({ ... });

If the blockId is required for a given transaction, it needs to be queried separately with getTransactionSummary helper:

import { getTransactionSummary } from 'fuels';

const transaction = await getTransactionSummary({
  id,
  provider,
});

Note: The blockId is still available in the result for a submitted transaction.

Optimize coin gql queries - #3301

  • The Provider.operations.getCoins() and Provider.operations.getCoinsToSpend function no longer return the owner. These methods shouldn't be called directly but are used internally to formulate responses from the SDK.

  • Removed the property owner from the Provider.operations.getCoinsToSpend() function. Suggest to use the owner from the input parameters.

October 13, 2024

Release v0.96.0

Checksum method to remove 0x before hashing - #3313

We fixed the checksum utilities:

  • Address.toChecksum()
  • Address.isChecksumValid()

Now, we correctly remove the leading 0x before hashing the address.

Because of this, previous values were invalid, and the update is required.

October 10, 2024

Release v0.95.0

Bump transaction pagination limit to 60 - #3306

  • A limit was added of 60 transactions to the provider.getTransactions() method.

Made Address toString and valueOf returns checksum - #3310

The return of both Address.toString() and Address.valueOf was modified to return the address checksum instead of the Bech32 string

// before
const address = new Address('fuel1elnmzsav56dqnp95sx4e2pckq36cvae9ser44m5zlvgtwxw49fmqd7e42e');

address.toString()
// fuel1elnmzsav56dqnp95sx4e2pckq36cvae9ser44m5zlvgtwxw49fmqd7e42e

address.valueOf()
// fuel1elnmzsav56dqnp95sx4e2pckq36cvae9ser44m5zlvgtwxw49fmqd7e42e
// after
const address = new Address('fuel1elnmzsav56dqnp95sx4e2pckq36cvae9ser44m5zlvgtwxw49fmqd7e42e');

address.toString()
// 0xEf86aFa9696Cf0dc6385e2C407A6e159A1103cEfB7E2Ae0636FB33d3cb2A9E4A

address.valueOf()
// 0xEf86aFa9696Cf0dc6385e2C407A6e159A1103cEfB7E2Ae0636FB33d3cb2A9E4A

Slim down chainInfoFragment and GasCostsFragment - #3286

  • latestBlock is no longer part of the ChainInfo return of provider.getChain(). You can fetch it via provider.getBlock('latest').
  • ChainInfo['consensusParameters']['gasCosts'] has been slimmed down to only contain data necessary for the operation of the SDK. Up until now, the SDK was fetching more than it needed. If this change affects you, you will have to create a custom graphql query for gasCosts for the additional data you need.

Optimize balance queries - #3296

  • Removed the owner and assetId properties from the response of Provider.operations.getBalance(). These properties are also required arguments to execute the function so are redundant in the response. Should you require these values, you should take them from the values that you passed to the function.
  • Removed the owner property from the response of Provider.operations.getBalances(). This property is a required argument to execute the function so is redundant in the response. Should you require this value, you should take it from the value that you passed to the function.

August 30, 2024

Release v0.94.0

Consider message on resources cache - #2872

The provider option flag cacheUtxo was renamed to resourceCacheTTL

// before
const provider = await Provider.create(FUEL_NETWORK_URL, {
  cacheUtxo: 5000,
});


using launched = await launchTestNode({
  providerOptions: {
    cacheUtxo: 5000,
  },
});
// after
const provider = await Provider.create(FUEL_NETWORK_URL, {
  resourceCacheTTL: 5000,
});

using launched = await launchTestNode({
  providerOptions: {
    resourceCacheTTL: 5000,
  },
});

Prettify typegen api - #2824

Predicate class

  • Predicate class constructor parameters renamed: inputData > data
// before
import { Predicate } from 'fuels';

const predicate = new Predicate({
  ...unchangedParameters,
  inputData,
});
// after
import { Predicate } from 'fuels';

const predicate = new Predicate({
  ...unchangedParameters,
  data,
});
  • Typegen extended/generated Predicate now accepts a single parameter for initialization
// before
import { TestPredicateAbi__factory } from './typegend';

TestPredicateAbi__factory.createInstance(provider, data, configurableConstants);
// after
import { TestPredicate } from './typegen';

new TestPredicate({
  provider,
  data,
  configurableConstants
});

launchTestNode utility

  • Renamed contractsConfigs[].deployer to contractsConfigs[].factory
  • Removed contractsConfigs[].bytecode and .hex.ts file

The bytecode is now saved within the factory class, so you don't have to deal with it.

// before
import { TokenAbi__factory } from './typegend';
import TokenAbiHex from './typegend/contracts/TokenAbi.hex';

using launched = await launchTestNode({
  contractsConfigs: [{
    deployer: TokenAbi__factory,
    bytecode: TokenAbiHex
  }],
});
// after
import { TokenFactory } from './typegend';

using launched = await launchTestNode({
  contractsConfigs: [{
    factory: TokenFactory,
  }],
})

Renamed method deployContract to deploy

Removed the redundant suffix on the ContractFactory class method name.

// before
import { ContractFactory } from 'fuels';

const factory = new ContractFactory(wallet);

factory.deployContract();
// after
import { ContractFactory } from 'fuels';

const factory = new ContractFactory(wallet);

factory.deploy();

Typegen Contract template

  • Removed Abi__factory suffix from class names
  • The file <name>.hex was removed (access it via <Name>.bytecode)
  • The files <name>__factory.ts and <name>.d.dts were merged into <name>.ts
  • The class <Name> and the interface <Name>Abi are now just <Name>
  • Method <Name>Factory.deployContract() renamed to <Name>Factory.deploy()
  • You may need to remove the previously generated <typegenDir>/contracts/factories directory
// before
import { TestContractAbi, TestContract__factory } from './typegen'
import testContractBytecode from './typegen/contracts/TestContract.hex'

const instance = await TestContract__factory.connect(id, wallet);

const deploy = await TestContract__factory.deployContract(testContractBytecode, wallet);
const { contract } = await deploy.waitForResult();
// after
import { TestContract, TestContractFactory } from './typegen'

const instance = new TestContract(id, wallet);

const deploy = await TestContractFactory.deploy(wallet);
const { contract } = await deploy.waitForResult();

Typegen Predicate template

  • Removed Abi__factory suffix from class names
  • Started accepting a single parameter object in constructor
  • You may need to remove the previously generated <typegenDir>/predicates/factories directory
// before
import { TestPredicateAbi__factory } from './typegen'

const predicate = TestPredicateAbi__factory.createInstance(provider);
// after
import { TestPredicate } from './typegen'

const predicate = new TestPredicate({ provider });

Typegen Script template

  • Removed Abi__factory suffix from class names
  • You may need to remove the previously generated <typegenDir>/scripts/factories directory
// before
import { TestPredicateAbi__factory } from './typegen'

const script = TestScriptAbi__factory.createInstance(wallet);
// after
import { TestPredicate } from './typegen'

const script = new TestScript(wallet);

Non-blocking blob deployment - #2929

The transaction ID from a contract deployment is now returned as a promise.

// before
import { ContractFactory } from 'fuels';

const factory = new ContractFactory(bytecode, abi, wallet);
const { waitForResult, contractId, transactionId } = await factory.deploy();
console.log(transactionId); // 0x123....
// after
import { ContractFactory } from 'fuels';

const factory = new ContractFactory(bytecode, abi, wallet);
const { waitForResult, contractId, waitForTransactionId } = await factory.deploy();
const transactionId = await waitForTransactionId();
console.log(transactionId); // 0x123....

Improve () and Option<T> type handling - #2777

  • () and Option<T> Sway types are now either required or optional, dependent on where the argument appears in the function arguments.

Given these Sway functions:

fn type_then_void_then_type(x: u8, y: (), z: u8) -> ()
fn type_then_void_then_void(x: u8, y: (), z: ()) -> ()

fn type_then_option_then_type(x: u8, y: Option<u8>, z: u8) -> ()
fn type_then_option_then_option(x: u8, y: Option<u8>, z: Option<u8>) -> ()

This is what changes:

// before
contract.functions.type_then_void_then_type(42, 43)
contract.functions.type_then_void_then_void(42) // Unchanged

contract.functions.type_then_option_then_type(42, undefined, 43)
contract.functions.type_then_option_then_option(42, undefined, undefined)
// after
contract.functions.type_then_void_then_type(42, undefined, 43)
contract.functions.type_then_void_then_void(42) // Unchanged

contract.functions.type_then_option_then_type(42, undefined, 43)
contract.functions.type_then_option_then_option(42)

fuel-core@0.32.1 and large contract deployments - #2827

MAX_CONTRACT_SIZE is no longer exported, it should now be fetched from the chain.

// before
import { MAX_CONTRACT_SIZE } from 'fuels';
// after
import { Provider, FUEL_NETWORK_URL } from 'fuels';

const provider = await Provider.create(FUEL_NETWORK_URL);
const { consensusParameters } = provider.getChain();
const maxContractSize = consensusParameters.contractParameters.contractMaxSize.toNumber();

Deprecate FUEL_NETWORK_URL and LOCAL_NETWORK_URL- #2915

Removed FUEL_NETWORK_URL constant.

// before
import { FUEL_NETWORK_URL } from 'fuels';

const provider = await Provider.create(FUEL_NETWORK_URL);
// after
const provider = await Provider.create('https://127.0.0.1:4000/v1/graphql');

Removed LOCAL_NETWORK_URL constant.

// before
import { LOCAL_NETWORK_URL } from 'fuels';

const provider = await Provider.create(LOCAL_NETWORK_URL);
// after
const provider = await Provider.create('https://127.0.0.1:4000/v1/graphql');

Integrate launchTestNode in remaining packages - #2811

Removed generateTestWallet and seedTestWallet utilities.

// before
import { bn } from "@fuel-ts/math";
import {
  seedTestWallet,
  generateTestWallet,
} from "@fuel-ts/account/test-utils";

const provider = await Provider.create("http://127.0.0.1:4000/v1/graphql");

// seeding
const walletA = Wallet.fromPrivateKey("0x...", provider);
const baseAssetId = provider.getBaseAssetId();
seedTestWallet(wallet, [{ assetId: baseAssetId, amount: bn(100_000) }]);

// generating
const walletB = await generateTestWallet(provider, [[1_000, baseAssetId]]);
// after
import { launchTestNode } from 'fuels/test-utils';

// create two wallets seeded with 100_000 units of the base asset
using launched = await launchTestNode({
  walletsConfig: {
    count: 2,
    amountPerCoin: 100_000,
  },
});

const {
  wallets: [walletA, walletB]
} = launched;

const balance = await walletA.getBalance() // 100_000

Removed launchNodeAndGetWallets utility.

// before
import { launchNodeAndGetWallets } from 'fuels/test-utils';

const { provider, wallets } = await launchNodeAndGetWallets();
// after
import { launchTestNode } from 'fuels/test-utils';

using launched = await launchTestNode();

const { provider, wallets } = launched;

Renamed AssetId to TestAssetId- #2905

Renamed testing class AssetId to TestAssetId.

// before
import { AssetId } from 'fuels/test-utils';

const [assetA] = AssetId.random();
// after
import { TestAssetId } from 'fuels/test-utils';

const [assetA] = TestAssetId.random();

Adding abi transpiler - #2856

New ABI spec

The SDK now adheres to the new specs introduced via:

  • https://github.com/FuelLabs/fuel-specs/pull/596
  • https://github.com/FuelLabs/fuel-specs/pull/599

Check these out to understand all its changes.

The class AbiCoder is no longer exported, and the way to do encoding and decoding of specific types is now via the Interface.encodeType and Interface.decodeType methods:

// before
const abi = yourAbi;
const functionArg = abi.functions.inputs[0];

const encoded = AbiCoder.encode(abi, functionArg, valueToEncode);
const decoded = AbiCoder.decode(abi, functionArg, valueToDecode, 0);
// after
import { Interface } from 'fuels';

const abi = yourAbi;
const functionArg = abi.functions.inputs[0];

const abiInterface = new Interface(abi);

const encoded = abiInterface.encodeType(functionArg.concreteTypeId, valueToEncode);
const decoded = abiInterface.decodeType(functionArg.concreteTypeId, valueToDecode);

Previously, you could get a type from the ABI via the Interface.findTypeById. This method has been removed after introducing the new abi specification because the concept of a type has been split into concrete types and metadata types. If you want a specific type, you can get it directly from the ABI.

// before
const abiInterface = new Interface(abi);

// internally this method searched the abi types:
// abi.types.find(t => t.typeId === id);
const type = abiInterface.findTypeById(id);
// after
import { Interface } from 'fuels';

// search the types on the abi directly
const concreteType = abi.concreteTypes.find(ct => ct.concreteTypeId === id);
const metadataType = abiInterface.jsonAbi.metadataTypes.find(mt => mt.metadataTypeId === id);

The JsonAbiArgument type isn't part of the new ABI spec (#596, #599) as such so we stopped exporting it. Its closest equivalent now would be a concrete type because it fully defines a type.

// before
const arg: JsonAbiArgument = {...};
// after
import { Interface } from 'fuels';

type ConcreteType = JsonAbi["concreteTypes"][number]
const arg: ConcreteType = {...};

Read malleable fields from transaction status on subscription - #2962

Removed TransactionResult.gqlTransaction. You can use the TransactionResult.transaction field instead, which has all the data that TransactionResult.gqlTransaction has but already decoded.

// before
const { gqlTransaction } = await new TransactionResponse('your-tx-id').waitForResult();
// after
const { transaction } = await new TransactionResponse('your-tx-id').waitForResult();

Fix assembly process for account transfer operation - #2963

The getTransferOperations helper function now requires an additional baseAssetId parameter.

// before
const transferOperations = getTransferOperations({ inputs, outputs, receipts })
// after
const transferOperations = getTransferOperations({ inputs, outputs, receipts, baseAssetId })

Wrap subscriptions in promise - #2964

// before
const subscription = provider.operations.statusChange({ transactionId });
for await (const response of subscription) { ... }
// after
const subscription = await provider.operations.statusChange({ transactionId });
for await (const response of subscription) { ... }

July 30, 2024

Release v0.93.0

Deploy contract validation - #2796

ErrorCode.INVALID_TRANSACTION_TYPE was migrated to ErrorCode.UNSUPPORTED_TRANSACTION_TYPE.

// before
const code = ErrorCode.INVALID_TRANSACTION_TYPE;
// after
const code = ErrorCode.UNSUPPORTED_TRANSACTION_TYPE;

Remove awaitExecution functionality - #2820

It is no longer possible to submit transactions using the awaitExecution flag and wait for the transaction to be processed at submission:

// before
const response = await account.sendTransaction(transactionRequest, { awaitExecution: true });
// after
const submit = await account.sendTransaction(transactionRequest);

const response = await submit.waitForResult();

Refactored the getTransactionCost method - #2643

Refactored functionality for Provider.getTransactionCost to Account.getTransactionCost and changed estimation parameter from quantitiesToContract to quantities.

// before
const provider = Provider.create(...);
const account = Wallet.generate({ ... }) || new Predicate(...);
const quantities: Array<CoinQuantityLike> = [
  { amount: 1000, assetId: provider.getBaseAssetId() }
];

const cost = provider.getTransactionCost(txRequest, {
  resourceOwner: account,
  quantitiesToContract: quantities,
})
// after
const provider = Provider.create(...);
const account = Wallet.generate({ ... }) || new Predicate(...);
const quantities: Array<CoinQuantityLike> = [
  { amount: 1000, assetId: provider.getBaseAssetId() }
];

const cost = account.getTransactionCost(txRequest, { quantities });

July 11, 2024

Release v0.92.0

Implement non-blocking contract call - #2692

The call method in the BaseInvocationScope class no longer waits for transaction execution, making it non-blocking. This change affects how transaction responses are handled.

// before
const { logs, value, transactionResult } = await contract.functions.xyz().call()
// after
const { transactionId, waitForResult } = await contract.functions.xyz().call();

const { logs, value, transactionResult } = await waitForResult();

Made deployContract a non-blocking call - #2597

The deployContract method no longer returns the contract instance directly. Instead, it returns an object containing the transactionId , the contractId, and a waitForResult function.

// before
const factory = new ContractFactory(contractByteCode, contractAbi, wallet);

const contract = await factory.deployContract();

const { value } = await contract.functions.xyz().call();

// after
const factory = new ContractFactory(contractByteCode, contractAbi, wallet);

const { waitForResult, transactionId, contractId } = await factory.deployContract();

const { contract, transactionResult } = await waitForResult();

const { value } = await contract.functions.xyz().call();

Implement pagination for Account methods - #2408

// before
const coins = await myWallet.getCoins(baseAssetId);
const messages = await myWallet.getMessages();
const balances = await myWallet.getBalances();
const blocks = await provider.getBlocks();

// after
const { coins, pageInfo } = await myWallet.getCoins(baseAssetId);
const { messages, pageInfo } = await myWallet.getMessages();
const { balances } = await myWallet.getBalances();
const { blocks, pageInfo } = await provider.getBlocks();

/*
  The `pageInfo` object contains cursor pagination information one
  can use to fetch subsequent pages selectively and on demand.
*/

launchNode.cleanup not killing node in last test of test group - #2718

The killNode and KillNodeParams functionality has been internalized and the method and interface have been deleted so they're no longer exported. It's marked as a breaking change for pedantic reasons and there shouldn't really be any affected users given that they kill nodes via cleanup which is unchanged, so no migration notes are necessary.

Remove InvocationResult from program package - #2652

The classes FunctionInvocationResult, InvocationCallResult, and InvocationResult have been removed. This change will not affect most users as the response for a contract call or script call remains the same; only the type name has changed.

// before
const callResult: FunctionInvocationResult = await contract.functions.xyz().call()

const dryRunResult: InvocationCallResult = await contract.functions.xyz().get()
const dryRunResult: InvocationCallResult = await contract.functions.xyz().dryRun()
const dryRunResult: InvocationCallResult = await contract.functions.xyz().simulate()


// after
const callResult: FunctionResult = await contract.functions.xyz().call()

const dryRunResult: DryRunResult = await contract.functions.xyz().get()
const dryRunResult: DryRunResult = await contract.functions.xyz().dryRun()
const dryRunResult: DryRunResult = await contract.functions.xyz().simulate()

Beta 3-5 Testnet Breaking Change Guide (Archive)

April 30, 2024

Sway

Release: Sway v0.56.0

The std::call_frames::second_param function now returns a u64 instead of a generic type T.

contract_id() has been removed in favor of ContractId::this().

#![allow(unused)]
fn main() {
/* BEFORE */
let contract_id = contract_id();

/* AFTER */
let contract_id = ContractId::this();
}

call_with_function_selector_vec has been removed in favor of call_with_function_selector.

#![allow(unused)]
fn main() {
/* BEFORE */
pub fn call_with_function_selector_vec(
  target: ContractId,
  function_selector: Vec<u8>,
  calldata: Vec<u8>,
  single_value_type_arg: bool,
  call_params: CallParams
) {...}

/* AFTER */
pub fn call_with_function_selector_vec(
  target: ContractId,
  function_selector: Bytes // new
  calldata: Bytes, // new
  call_params: CallParams
) {...}
}

The BASE_ASSET_ID constant has been removed, and AssetId::base_asset_id() is now AssetId::base().

#![allow(unused)]
fn main() {
/* BEFORE */
let base_asset_id = BASE_ASSET_ID;
/* OR */
let base_asset_id = AssetId::base_asset_id();

/* AFTER */
let base_asset_id = AssetId:base();
}

You can no longer access the following:

  • force_transfer_to_contract()
  • transfer_to_address()
  • mint_to_contract()
  • mint_to_address()

Instead use the transfer(), mint(), and mint_to() functions accordingly.

#![allow(unused)]
fn main() {
/* BEFORE */
let user = Address:from(address);
mint_to_address(user, ZERO_B256, amount);

/* AFTER */
mint_to(Identity::Address(user), ZERO_B256, amount);
}

The new encoding (encoding v1) is now set by default. If you would like to build your forc project without v1 encoding then run the following command:

forc build --no-encoding-v1

run_external in sway-lib-std has been removed until LDC is stabilized.

Release: Sway v0.55.0

GTF constants along with the following functions have been removed to match the current Fuel VM instruction set:

  • input_maturity()
  • tx_receipts_root()

tx_gas_price has been renamed tx_tip

#![allow(unused)]
fn main() {
/* BEFORE */
let gas_price = tx_gas_price();

/* AFTER */
let tip = tx_tip();
}

Release: Sway v0.54.0

The forc-client and forc-tx plugins now take u16 instead of u8 for witness index types.

TS-SDK

Release v0.83.0

BaseAssetId is no longer exported by fuels. It can be fetched from a Provider.

/* BEFORE */
import { BasedAssetId } from "fuels";

/* AFTER */
const provider = await Provider.create(FUEL_NETWORK_URL);
const baseAssetId = provider.getBaseAssetId();

TransactionRequest.addCoinOutput and TransactionRequest.addChangeOutput now requires an assetId, it no longer defaults to the BaseAssetId.

TransactionRequest.fundWithFakeUtxos now requires passing the baseAssetId as a function parameter. This is the only function that is base asset aware, so that it can be used specifically to estimate the transaction cost.

CoinQuantityLike now requires an AssetId. Previously most of it's usages would default to the BaseAssetId, as this must now be fetched, so it must be passed to the type.

/* BEFORE */
let coin: CoinQuantityLike = [1000];
coin = { amount: 1000 };

/* AFTER */
const assetId = "0x..";
let coin: CoinQuantityLike = [1000, assetId];
coin = { amount: 1000, assetId };

gasPrice is calculated by the VM so we do not need use it anymore.

/* BEFORE */
await factory.deployContract({ gasPrice });

/* AFTER */
await factory.deployContract();

PolicyType.GasPrice is now PolicyType.Tip.

The Account.fund function parameters changed. Also the information returned by Provider.getTransactionCost is useful here because it can be passed as the second parameter to Account.fund.

/* BEFORE */
async fund<T extends TransactionRequest>(
    request: T,
    coinQuantities: CoinQuantity[],
    fee: BN,
    inputsWithEstimatedPredicates: TransactionRequestInput[],
    addedSignatures?: number
): Promise<T>

/* AFTER */
export type EstimatedTxParams = {
  maxFee: BN;
  estimatedPredicates: TransactionRequestInput[];
  addedSignatures: number;
  requiredQuantities: CoinQuantity[];
}
async fund<T extends TransactionRequest>(request: T, params: EstimatedTxParams): Promise<T>

GraphQL URL now includes a versioning path: http://127.0.0.1:4000/v1/graphql.

calculateTransactionFee now requires tip, maxGasPerTx, and gasPrice. Also gasUsed is not used anymore.

/* BEFORE */
const { fee } = calculateTransactionFee({
  gasUsed,
  rawPayload,
  consensusParameters: {
    gasCosts,
    feeParams: {
      gasPerByte,
      gasPriceFactor,
    },
  },
});

/* AFTER */
const { fee } = calculateTransactionFee({
  gasPrice, // new
  tip, // new
  consensusParameters: {
    maxGasPerTx, // new
    gasCosts,
    feeParams: {
      gasPerByte,
      gasPriceFactor,
    },
  },
  rawPayload,
});

Due to forc upgrade v0.52.0 AssetId and EvmAddress property value was renamed to bits

/* BEFORE */
export type EvmAddress = {
  value: B256AddressEvm;
};
export type AssetId = {
  value: B256Address;
};

/* AFTER */
export type EvmAddress = {
  bits: B256AddressEvm;
};
export type AssetId = {
  bits: B256Address;
};

Release v0.80.0

Removed unused property usedFee from Provider.getTransactionCost response.

Renamed getAssetId to getMintedAssetId.

Rust SDK

Release v0.58.0

The new encoding is now the default encoding. Use the following command if you would like to run your cargo tests with the legacy encoding.

cargo test --features legacy_encoding

Release v0.57.0

The BASE_ASSET_ID constant has been removed and replaced by a new Provider function.

#![allow(unused)]
fn main() {
/* BEFORE */
let base_asset_id = BASE_ASSET_ID;

/* AFTER */
let base_asset_id = provider.base_asset_id();
}

Config was renamed to NodeConfig

#![allow(unused)]
fn main() {
/* BEFORE */
let node_config = Config::default();

/* AFTER */
let node_config = NodeConfig::default();
}

FuelService::start() now accepts NodeConfig, ChainConfig, and StateConfig as arguments to startup a node.

#![allow(unused)]
fn main() {
/* BEFORE */
let server = FuelService::start(Config::default()).await?;

/* AFTER */
let server = FuelService::start(
  NodeConfig::default(),
  ChainConfig::default(),
  StateConfig::default(),
)
.await?;
}

When instantiating ConsensusParameters you must make use of setters.

#![allow(unused)]
fn main() {
/* BEFORE */
let consensus_parameters = ConsensusParameters {
    tx_params,
    fee_params,
    ..Default::default()
};

/* AFTER */
let mut consensus_parameters = ConsensusParameters::default();
consensus_parameters.set_tx_params(tx_params);
consensus_parameters.set_fee_params(fee_params);
}

Fields now need to be accessed via methods.

#![allow(unused)]
fn main() {
/* BEFORE */
let chain_id = consensus_parameters.chain_id;

/* AFTER */
let chain_id = consensus_parameters.chain_id();
}

The same applies to other parameter structs used when setting up a node, such as TxParameters, ContractParameters, PredicateParameters etc.

The witness_index parameters in CreateTransactionBuilder::with_bytecode_witness_index are now a u16.

NodeInfo no longer has min_gas_price.

CreateTransaction no longer has bytecode_length().

Header no longer has message_receipt_root, but gains:

#![allow(unused)]
fn main() {
pub message_outbox_root: Bytes32,
pub event_inbox_root: Bytes32,
pub consensus_parameters_version: u32,
pub state_transition_bytecode_version: u32
}

March 27, 2024

Sway

Release Sway v0.52.0

The bytes field on B512 and the value field EvmAddress have been renamed bits, made private, and made accessible via bits().

#![allow(unused)]
fn main() {
/* BEFORE */
let bytes = myB12.bytes;
let value = myEvmAddress.value;

/* AFTER */
let bits = myB512.bits();
let bits = myEvmAddress.bits();
}

The fields on U128 have been made private.

#![allow(unused)]
fn main() {
/* BEFORE */
let zero_u128 = U128 { upper: 0, lower: 0 };
let upper = zero_u128.upper;

/* AFTER */
let zero_u128 = U128::from(0, 0);
let upper = zero_u128.upper();
}

The fields on StorageKey have been made private and are now accessed via:

  • new()
  • slot()
  • offset()
  • field_id()

The following heap types have been updated to have private struct variables:

  • Bytes
  • RawBytes
  • Vec
  • RawVec
  • String
#![allow(unused)]
fn main() {
/* BEFORE */
let bytes_ptr = bytes.buf.ptr();

/* AFTER */
let bytes_ptr = bytes.ptr();
}

The value field on AssetId, ContractId, and Address is now private and renamed bits.

#![allow(unused)]
fn main() {
/* BEFORE */
let value = assetId.value;

/* AFTER */
let bits = assetId.bits();
}

predicate_id() has been renamed to predicate_address().

#![allow(unused)]
fn main() {
/* BEFORE */
let predicate = predicate_id();

/* AFTER */
let predicate = predicate_address();
}

U256 has been removed use the native u256 type instead.

#![allow(unused)]
fn main() {
/* BEFORE */
let my_U256 = U256::max();

/* AFTER */
let my_u256 = u256::max();
}

TS-SDK

Release v0.79.0

externalLoggedTypes has been removed from the Interface class.

Release v0.77.0

Predicate data is now accepted on the Predicate constructor.

/* BEFORE */
const predicate = new Predicate(bytecode, provider, abi, configurableConstants);

/* AFTER */
const predicate = new Predicate({
  bytecode,
  abi, // optional
  provider,
  inputData, // optional
  configurableConstants, // optional
});

The setData method has been removed from Predicate. If you want to pass in predicate data after instantiating the Predicate or if you want to use different data than what was passed to the constructor, then you will have to create a new Predicate instance.

Rust SDK

Release v0.56.0

Experimental encoding for logs was added. Use the following command to run your tests with the experimental encoding

cargo test --features experimental

NOTE experimental encoding is now the default encoding in v0.57.0

Configurables structs now need to be instantiated through a ::new(encoder_config) or ::default() method.

#![allow(unused)]
fn main() {
/* BEFORE */
let configurables = MyContractConfigurables::new().with_STRUCT(my_struct);

/* AFTER */
let configurables = MyContractConfigurables::default().with_STRUCT(my_struct);
/* OR */
let configurables = MyContractConfigurables::new(encoder_config).with_STRUCT(my_struct);
}

Configurables::with_some_string_config(some_string) methods now return a Result<Configurables> instead of Configurables.

Predicates::encode_data now returns a Result<UnresolvedBytes> instead of UnresolvedBytes.

AbiEncoder structs must be instantiated through a ::new(encoder_config) or ::default() method.

#![allow(unused)]
fn main() {
/* BEFORE */
let encoded = ABIEncoder::encode(&args).resolve(0);

/* AFTER */
let encoded = ABIEncoder::default().encode(&args).resolve(0);
/* OR */
let encoded = ABIEncoder::new(config).encode(&args).resolve(0);
}

EnumVariants are now imported through param_types::EnumVariants.

#![allow(unused)]
fn main() {
/* BEFORE */
use fuels::types::enum_variants::EnumVariants;

/* AFTER */
use fuels::types::param_types::EnumVariants;
}

TxPolicies gas_price is replaced with tip

#![allow(unused)]
fn main() {
/* BEFORE */
let tx_policies = TXPolicies::default().with_gas_price(1);

/* AFTER */
let tx_policies = TXPolicies::default().with_tip(1);
}

checked_dry_run has been removed from Provider.

dry_run now returns Result<TxStatus> instead of Result<Vec<Receipt>>. The receipts can be taken with tx_status.take_receipts().

TransactionResponse's block_id is replaced with block_height.

estimate_transaction_cost has a new argument block_horizon: Option<u32>.

#![allow(unused)]
fn main() {
/* BEFORE */
let transaction_cost = contract_instance
  .methods()
  .my_contract_call()
  .estimate_transaction_cost(Some(tolerance))
  .await?;

/* AFTER */
let transaction_cost = contract_instance
  .methods()
  .my_contract_call()
  .estimate_transaction_cost(tolerance, block_horizon)
  .await?;
}

February 22, 2024

Sway

Release: Sway v0.51.0

You can no longer access private fields in structs.

#![allow(unused)]
fn main() {
/* BEFORE */
let private = my_struct.private // just a warning

/* AFTER */
let private = my_struct.private // ERROR
let private = my_struct.private() // you must create a function to access private variables
}

The Never type is now !.

#![allow(unused)]
fn main() {
/* BEFORE */
let x: NEVER = { return 123 };

/* AFTER */
let x: ! = { return 123 };
}

Release: Sway v0.50.0

Configurables are now forbidden in const expressions.

// Not allowed
script;

configurable {
    VALUE: u64 = 42,
}

fn main() {
    const CONSTANT: u64 = VALUE;
}

Struct fields are now private by default. You must explicitly mark a field public.

#![allow(unused)]
fn main() {
/* BEFORE */
pub struct Struct {
  public_field: u8,
}

/* AFTER */
pub struct Struct {
  pub public_field: u8,
  private_field: u8,
}
}

The From trait has been redesigned into the From/Into rust-like trait pair.

#![allow(unused)]
fn main() {
/* BEFORE */
impl From<b256> for Address {
  fn from(bits: b256) -> Self {
    Self { value: bits }
  }

  fn into(self) -> b256 {
    self.value
  }
}

let address = Address::from(ZERO_B256);
let b256_data = address.into();

/* AFTER */
impl From<b256> for Address {
  fn from(bits: b256) -> Self {
    Self { value: bits }
  }
}

impl From<Address> for b256 {
  fn from(address: Address) -> b256 {
    address.value
  }
}

let address = Address::from(ZERO_B256);
let b256_data: b256 = address.into();
let address: Address = b256_data.into();
}

TS-SDK

Release: v0.74.0

Provider has been removed from WalletManager types.

/* Before */
const vault = new MnemonicVault({
  secret: mnemonic,
  provider,
});

/* After */
const vault = new MnemonicVault({
  secret: mnemonic,
});

The Account and account related packages have been restructured. Anything imported from the following packages will now be imported from @fuel-ts/account

  • @fuel-ts/hdwallet
  • @fuel-ts/mnemonic
  • @fuel-ts/predicate
  • @fuel-ts/providers
  • @fuel-ts/signer
  • @fuel-ts/wallet-manager
  • @fuel-ts/wallet
  • @fuel-ts/wordlists

February 5, 2024 (Beta 5)

Sway

Release: Sway v0.49.2

Numerous elements in the standard library have undergone changes. The token.sw file has been renamed to asset.sw, impacting the transfer() and mint_to() functions. This modification aims to bring about greater consistency across all functions related to asset management.

/* BEFORE - v0.46.0 */
use std::{
    token::transfer

};
/* AFTER - v0.49.2 */
use std::{
    asset::transfer
};

The instructions LW (Load Word) and SW (Store Word) are now replaced with LB (Load Byte) and SB (Store Byte), specifically for smaller data types like u8. This adjustment allows these types to be contained within a single byte, rather than occupying a full word. However, for other data types such as u16, the original instruction format remains unchanged.

/* BEFORE - v0.46.0 */
sw   output r1 i0;

/* AFTER - v0.49.2 */
sb   output r1 i0;

DEFAULT_SUB_ID has been introduced to improve UX. It is equivalent to the ZERO_B256 constant.

/* BEFORE - v0.46.0 */
use std::call_frames::contract_id;

fn foo(other_contract: ContractId) {
     let other_asset = AssetId::default(other_contract);
     let my_asset = AssetId::default(contract_id());
}

/* AFTER - v0.49.2 */
fn foo(other_contract: ContractId) {
     let other_asset = AssetId::new(other_contract, DEFAULT_SUB_ID);
     let my_asset = AssetId::default();
}

The Eq trait now exists for Option.

/* BEFORE - v0.46.0 */
let option1: Option<u64> = Some(5);
let option2: Option<u64> = Some(5);

match (option1, option2) {
    (Some(a), Some(b)) => a == b,
    (None, None) => true,
    _ => false,
}

/* AFTER - v0.49.2 */
let option1: Option<u64> = Some(5);
let option2: Option<u64> = Some(5);

if option1 == option2 {
    return true
} else {
    return false
}

The standard library tx introduces several new functions including tx_max_fee(), tx_witness_limit(), script_gas_limit(), and policies(). tx_gas_limit() has been deprecated to support the new TxPolicy, replacing TxParameters.

/* BEFORE - v0.46.0 */
fn get_tx_gas_limit() -> u64;

/* AFTER - v0.49.2 */
fn get_script_gas_limit() -> u64;

The existing functions inside the standard library tx, including tx_gas_price() and tx_maturity(), now return Option<u64> and Option<u32> respectively, instead of just u64 and u32.

/* BEFORE - v0.46.0 */
fn get_tx_maturity() -> u32 {
    tx_maturity()
}

/* AFTER - v0.49.2 */
fn get_tx_maturity() -> u32 {
    tx_maturity().unwrap()
}

Along with these changes, GTF opcodes have been updated in the following standard libraries.

  1. inputs.sw
  2. outputs.sw
  3. tx.sw

Byte conversions and array conversions for u256, u64, u32, u16, and b256 have been introduced into the standard library.

  1. Byte conversions
/* AFTER - v0.49.2 */
fn foo() {
  let x: u16 = 513;
  let result = x.to_le_bytes();

  assert(result[0] == 1_u8);
  assert(result[1] == 2_u8);
}
  1. Array conversions
/* AFTER - v0.49.2 */
fn foo() {
  let x: u16 = 513;
  let result = x.to_le_bytes();

  assert(result.get(0).unwrap() == 1_u8);
  assert(result.get(1).unwrap() == 2_u8);
}

Power uses a u32 instead of self

/* BEFORE - v0.46.0 */
assert(2u16.pow(2u16) == 4u16);

/* AFTER - v0.49.2 */
assert(2u16.pow(2u32) == 4u16);

TS SDK

Release: TS SDK v0.73.0

Several fuel-core configuration-related options have been removed from the LaunchNodeOptions. These include: chainConfigPath, consensusKey, useInMemoryDb, and poaInstant. These options can now only be passed through the args property.

/* BEFORE - v0.60.0 */
const { cleanup, ip, port } = await launchNode({
  chainConfigPath,
  consensusKey = "0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298",
  args: defaultFuelCoreArgs,
});

/* AFTER - v0.73.0 */
const { cleanup, ip, port } = await launchNode({
  args: ["--poa-instant", "false", "--poa-interval-period", "400ms"],
});

Contract calls requires gasLimit and gasPrice to be specified in txParams().

/* BEFORE - v0.60.0 */
let resp = await contract.functions.count().simulate();

/* AFTER - v0.73.0 */
let resp = await contract.functions
  .count()
  .txParams({ gasPrice: 1, gasLimit: 100_000 })
  .simulate();

chainInfoCache and nodeInfoCache are now private methods, to prevent users from accessing invalid cached information after it becomes stale.

/* BEFORE - v0.60.0 */
Provider.chainInfoCache[FUEL_NETWORK_URL];
Provider.nodeInfoCache[FUEL_NETWORK_URL];

/* AFTER - v0.73.0 */
provider.getChain();
provider.getNode();

The switchURL() method, used to update the URL for the provider, is now named connect().

/* BEFORE - v0.60.0 */
await provider.switchUrl(altProviderUrl);

/* AFTER - v0.73.0 */
await provider.connect(altProviderUrl);

Support for new Sway types has been introduced with:

  1. Bytes
/* AFTER - v0.73.0 */
const bytes = [40, 41, 42];
const { value } = await contract.functions.bytes_comparison(bytes).simulate();
  1. Raw Slices
/* AFTER - v0.73.0 */
const rawSlice = [40, 41, 42];
const { value } = await contract.functions
  .raw_slice_comparison(rawSlice)
  .simulate();
  1. StdString
/* AFTER - v0.73.0 */
const stdString = "Hello World";
const { value } = await contract.functions
  .string_comparison(stdString)
  .simulate();

Typegen attempts to resolve, auto-load, and embed the Storage Slots for your Contract within the MyContract__factory class. However, you can override this, along with other options, when calling the deployContract method:

/* AFTER - v0.73.0 */
import storageSlots from "../contract/out/debug/storage-slots.json";

const contract = await MyContract__factory.deployContract(bytecode, wallet, {
  storageSlots,
});

concat, arrayify, and hexlify have been introduced to the utils to replace their respective functions from the ethers library, avoiding the reexporting of ethers functions.

/* BEFORE - v0.60.0 */
import { concat, arrayify, hexlify } from "@ethersproject/bytes";

const someBytes = concat([
  new Uint8Array([1, 2, 3]),
  new Uint8Array([4, 5, 6]),
  new Uint8Array([7, 8, 9]),
]);
const someHex = hexlify(new Uint8Array([0, 1, 2, 3]));
const someArray = arrayify(new Uint8Array([0, 1, 2, 3]));

/* AFTER - v0.73.0 */
import { concat, arrayify, hexlify } from "@fuel-ts/utils";

const someBytes = concat([
  new Uint8Array([1, 2, 3]),
  new Uint8Array([4, 5, 6]),
  new Uint8Array([7, 8, 9]),
]);
const someHex = hexlify(new Uint8Array([0, 1, 2, 3]));
const someArray = arrayify(new Uint8Array([0, 1, 2, 3]));

Address types can no longer be used directly to represent a b256 and must instead use the toB256() conversion method.

/* BEFORE - v0.60.0 */
const addressId = {
  value: userWallet.address,
};

tokenContract.functions
  .transfer_coins_to_output(addressId, assetId, amount)
  .call();

/* AFTER - v0.73.0 */
const addressId = {
  value: userWallet.address.toB256(),
};

tokenContract.functions
  .transfer_coins_to_output(addressId, assetId, amount)
  .call();

The Account class's fund() method now takes in two new parameters: quantities and fee, of types CoinQuantity[] and BN, respectively. These can be derived from the provider's getTransactionCost() method.

/* BEFORE - v0.60.0 */
await wallet.fund(transactionRequest);

/* AFTER - v0.73.0 */
const { maxFee, requiredQuantities } = await provider.getTransactionCost(
  transactionRequest
);

await wallet.fund(transactionRequest, quantities, fee);

The provider's getTransactionCost now breaks down its old fee into minFee, usedFee, and maxFee, based on the actual calculation of the transaction. Additionally, requiredQuantities, receipts, minGas, and maxGas, of types coinQuantity[], TransactionResultReceipt[], BN, and BN respectively, have also been introduced to improve the granularity of cost estimation.

/* BEFORE - v0.60.0 */
const { fee } = await this.account.provider.getTransactionCost(
  transactionRequest
);

/* AFTER - v0.73.0 */
const {
  requiredQuantities,
  receipts,
  minGas,
  maxGas,
  minFee,
  maxFee,
  usedFee,
} = await this.account.provider.getTransactionCost(transactionRequest);

The getTransferOperations function now takes in a receipts parameter as well, ensuring that contract transactions return the transfer asset.

/* BEFORE - v0.60.0 */
const operations = getTransferOperations({ inputs: [], outputs: [] });

/* AFTER - v0.73.0 */
const operations = getTransferOperations({
  inputs: [],
  outputs: [],
  receipts: [],
});

The predicate introduces a new getTransferTxId, a method to calculate the transaction ID for a Predicate.transfer transaction.

/* AFTER - v0.73.0 */
const txId = await predicate.getTransferTxId(address, amount, BaseAssetId, {
  gasPrice,
});

The deployContract method contains a new parameter, storageSlotsPath, to avoid issues that may arise if storage slots are not auto-loaded. Without auto-loading, some contracts will revert due to improper or missing initialization of storage slots.

/* BEFORE - v0.60.0 */
const assetId = BaseAssetId;

/* AFTER - v0.73.0 */
const assetId: AssetId = { value: BaseAssetId };

AssetId has been introduced to match the Sway standard library as a Struct wrapper around an inner Bits256 value.

Rust SDK

Release: Rust SDK v0.55.0

The sign_message() and sign_transaction functions in the Signer trait have been consolidated into a single method, now simply named sign.

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
let signature1: B512 = wallet.sign_message(data_to_sign).await?.as_ref().try_into()?;

/* AFTER - v0.55.0 */
let signature1: B512 = wallet.sign(data_to_sign).await?.as_ref().try_into()?;
}

The function check_without_signatures in the Transaction trait has been renamed to check. This updated check function retains its original capabilities and now includes the additional feature of checking with signatures.

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
tx.check_without_signatures(chain_info.latest_block.header.height, self.consensus_parameters())?;

/* AFTER - v0.55.0 */
tx.check(chain_info.latest_block.header.height, self.consensus_parameters())?;
}

The typo in the add_witnessses function name under the Account trait has been fixed and is now add_witnesses.

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
account.add_witnessses(&mut tb);

/* AFTER - v0.55.0 */
account.add_witnesses(&mut tb)?;
}

Use of Message, PublicKey, SecretKey and Signature can be found inside fuels::crypto:: now.

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
use fuels::accounts::fuel_crypto::SecretKey;

/* AFTER - v0.55.0 */
use fuels::crypto::SecretKey,
}

The submit_and_await_commit() function now returns a TxStatus instead of a TxId.

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
let tx_id = self.client.submit_and_await_commit(&tx.clone().into()).await?.into();

/* AFTER - v0.55.0 */
let tx_status = self.client.submit_and_await_commit(&tx.clone().into()).await?.into();
}

When constructing a transaction, the provider already possesses all the necessary information, rendering NetworkInfo and all its related functions and methods obsolete. Consequently, ScriptTransactionBuilder::new, CreateTransactionBuilder::new, and Provider::new have been removed for ::default().

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
use fuels_core::types::transaction_builders::{DryRunner, NetworkInfo}

ScriptTransactionBuilder::new(network_info)

/* AFTER - v0.55.0 */
use fuels_core::types::transaction_builders::{DryRunner}

ScriptTransactionBuilder::default()
}

In Sway, U256 has been deprecated in favor of u256. It is no longer supported in the SDK. Usage of U256 will now result in a runtime error.

TxPolicies supersedes TxParameters.

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
let tx_parameters = TxParameters::default()

/* AFTER - v0.55.0 */
let tx_policies = TxPolicies::default()
}

Three new optional fields have been introduced in TxPolicies:

  1. WitnessLimit, which sets a new restriction for transaction witnesses by introducing a limit on the maximum byte size of witnesses in transactions.
  2. MaxFee, which sets an upper limit on the transaction fee that a user is willing to pay.
  3. ScriptGasLimit, which no longer constrains predicate execution time but exclusively limits the gas limit of scripts. If this field is not set, the SDK will estimate gas consumption and set it automatically.

Additionally, GasPrice and Maturity fields within TxPolicies are now optional parameters.

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
let tx_parameters = TxParameters::new(gas_price, gas_limit, maturity)

/* AFTER - v0.55.0 */
let tx_policies = TxPolicies::new(Some(gas_price), Some(witness_limit), Some(maturity), Some(max_fee), Some(script_gas_limit))
}

TxPolicy Pitfalls

  1. If the max_fee is greater than policies.max_fee, then the transaction will be rejected.
  2. If the witnesses_size is greater than policies.witness_limit, then the transaction will be rejected.

The predicate's get_message_proof now uses nonce instead of msg_id.

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
let proof = predicate.try_provider()?
  .get_message_proof(&tx_id, &msg_id, None, Some(2))

/* AFTER - v0.55.0 */
let proof = predicate.try_provider()?
  .get_message_proof(&tx_id, &msg_nonce, None, Some(2))
}

When using local chain configs, the manual_blocks_enabled option is replaced by the new debug flag. Additionally, with local_node() being deprecated in favor of default(), the options utxo_validation and manual_blocks_enabled are enabled by default for the test providers.

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
let config = Config {
    utxo_validation: true,
    manual_blocks_enabled: true,
    ..Config::local_node()
};

/* AFTER - v0.55.0 */
let config = Config {
    ..Config::default()
};
}

When using transaction_builders, the BuildableTransaction trait must be in scope.

#![allow(unused)]
fn main() {
/* BEFORE - v0.48.0 */
use fuels_core::{
    types::{
        transaction_builders::{TransactionBuilder, ScriptTransactionBuilder},
    },
};

/* AFTER - v0.55.0 */
use fuels_core::{
    types::{
        transaction_builders::{BuildableTransaction, ScriptTransactionBuilder},
    },
};
}

October 2, 2023

TS SDK

Release: TS SDK v0.60.0

Provider is used so widely in our SDK, there are multiple breaking changes that we need to be aware of and need to communicate to our users:

/* BEFORE - v0.57.0 */
const provider = new Provider(url);

/* AFTER - v0.60.0 */
const provider = await Provider.create(url);

All of these methods now require a Provider to be passed in:

Wallet Methods

Some of these methods used to accept a URL instead of a Provider object. Note that the provider parameter has to be a Provider object now.

const provider = await Provider.create(url);
/* BEFORE - v0.57.0 */
WalletUnlocked.fromSeed(seed, path);

WalletUnlocked.fromMnemonic(mnemonic, path, passphrase);

WalletUnlocked.fromExtendedKey(extendedKey);
await WalletUnlocked.fromEncryptedJson(jsonWallet, password);

Wallet.fromAddress(address);

Wallet.fromPrivateKey(pk);

Wallet.generate();

/* AFTER - v0.60.0 */
WalletUnlocked.fromSeed(seed, provider, path);

WalletUnlocked.fromMnemonic(mnemonic, provider, path, passphrase);

WalletUnlocked.fromExtendedKey(extendedKey, provider);
await WalletUnlocked.fromEncryptedJson(jsonWallet, password, provider);

Wallet.fromAddress(address, provider);

Wallet.fromPrivateKey(pk, provider);

Wallet.generate({ provider });

'Account' Class

/* BEFORE - v0.57.0 */
const account = new Account(address);

/* AFTER - v0.60.0 */
const account = new Account(address, provider);

PrivateKeyVault

These are the options that are accepted by the PrivateKeyVault constructor. provider is now a required input.

/* BEFORE - v0.57.0 */
interface PkVaultOptions {
  secret?: string;
  accounts?: Array<string>;
}

/* AFTER - v0.60.0 */
interface PkVaultOptions {
  secret?: string;
  accounts?: Array<string>;
  provider: Provider;
}

MnemonicVault

/* BEFORE - v0.57.0 */
interface MnemonicVaultOptions {
  secret?: string;
  accounts?: Array<string>;
}

/* AFTER - v0.60.0 */
interface MnemonicVaultOptions {
  secret?: string;
  accounts?: Array<string>;
  provider: Provider;
}

WalletManager

/* BEFORE - v0.57.0 */
export type VaultConfig = {
  type: string;
  title?: string;
  secret?: string;
};

/* AFTER - v0.60.0 */
export type VaultConfig = {
  type: string;
  title?: string;
  secret?: string;
  provider: Provider;
};

Predicates

The provider is no longer optional. Note the change in parameter order, and that chainId is no longer required to be passed.

/* BEFORE - v0.57.0 */
const predicate = new Predicate(bytes, chainId, jsonAbi);

/* AFTER - v0.60.0 */
const predicate = new Predicate(bytes, provider, jsonAbi);

September 18, 2023

Sway

Release: Sway v0.46.0

From now on, string literals produce the str slice type instead of the string array type. To convert between string arrays and slices, you can use the newly provided intrinsics.

/* BEFORE - v0.45.0 */
let my_string: str[4] = "fuel";

/* AFTER - v0.46.0 */
let my_string: str = "fuel";

If you use a function that needs a specific trait and you don't import that trait, the compiler now will raise an error. This is because the compiler isn't aware of the trait in the current context.

For the example below you would now get an error if the Hash trait for u64 isn't imported. To solve this, ensure you import the "Hash" trait.

/* BEFORE - v0.45.0 */
storage {
    item_map: StorageMap<u64, Item> = StorageMap {},
}

/* AFTER - v0.46.0 */
use std::{
    hash::Hash,
};

storage {
    item_map: StorageMap<u64, Item> = StorageMap {},
}

TS SDK

Release: TS SDK v0.57.0

The addResourceInputsAndOutputs() function has been renamed to addResources(), streamlining its name.

/* BEFORE - v0.55.0 */
request.addResourceInputsAndOutputs(resources);

/* AFTER - v0.57.0 */
request.addResources(resources);

Similarly, addPredicateResourcesInputsAndOutputs() is now more concisely known as addPredicateResources().

The reason we have a distinct method for adding predicate resources is that the creation of predicate inputs mandates the presence of both the predicate's bytes and data bytes. With these methods, there's no longer a need to manually create and set up an instance of a ScriptTransactionRequest, simplifying the process further.

/* BEFORE - v0.55.0 */
const predicateInputs: TransactionRequestInput[] = predicateUtxos.map(
  (utxo) => ({
    id: utxo.id,
    type: InputType.Coin,
    amount: utxo.amount,
    assetId: utxo.assetId,
    owner: utxo.owner.toB256(),
    txPointer: "0x00000000000000000000000000000000",
    witnessIndex: 0,
    maturity: 0,
    predicate: predicate.bytes,
    predicateData: predicate.predicateData,
  })
);

/* AFTER - v0.57.0 */
request.addPredicateResources(
  predicateUtxos,
  predicate.bytes,
  predicate.predicateData
);

Rust SDK

Release: Rust SDK v0.48.0

The function calculate_base_amount_with_fee() currently returns a value of type Option<64>.

#![allow(unused)]
fn main() {
/* BEFORE - v0.47.0 */
let new_base_amount = calculate_base_amount_with_fee(&tb, &consensus_parameters, previous_base_amount)

/* AFTER - v0.48.0 */
let new_base_amount = calculate_base_amount_with_fee(&tb, &consensus_parameters, previous_base_amount)?
}

The function calculate_base_amount_with_fee() now returns a value of type Result<Option<TransactionFee>> instead of Option<TransactionFee>.

#![allow(unused)]
fn main() {
/* BEFORE - v0.47.0 */
let transaction_fee = tb.fee_checked_from_tx(consensus_params).expect("Error calculating TransactionFee");

/* AFTER - v0.48.0 */
let transaction_fee = tb.fee_checked_from_tx(consensus_params)?.ok_or(error!(InvalidData, "Error calculating TransactionFee"))?;
}

Storage slots are now automatically loaded in when using the default configuration.

#![allow(unused)]
fn main() {
/* BEFORE - v0.47.0 */
let storage_config =
StorageConfiguration::load_from("out/debug/contract-storage_slots.json").unwrap();

let load_config = LoadConfiguration::default().with_storage_configuration(storage_config);

let id = Contract::load_from(
    "./out/debug/contract.bin",
    load_config,
)
.unwrap()
.deploy(&wallet, TxParameters::default())
.await
.unwrap();

/* AFTER - v0.48.0 */
let id = Contract::load_from(
    "./out/debug/contract.bin",
    LoadConfiguration::default(),
)
.unwrap()
.deploy(&wallet, TxParameters::default())
.await
.unwrap();
}

Verified Assets

Using this section

You can find the current list of verified assets maintained by Fuel here: verified-assets.json

Projects are welcome to use this information, but please note that it is provided at your own risk.

Additionally, you can download the latest asset information and icons in a single archive. This is useful if you want to locally cache the list or include it in a release pipeline for your tools and libraries: verified-assets.zip

For more information, please visit the verified assets repository here.

Ethereum Sepolia Testnet

Ethereum Foundry

NameAddressDecimals
Ethereum18

Ethereum L1

Fuel Devnet

NameAsset IDContract AddressDecimals
Ethereum0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad079

Fuel Testnet

NameAsset IDContract AddressDecimals
Ethereum0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad079
Fuel0x324d0c35a4299ef88138a656d5272c5a3a9ccde2630ae055dacaf9d13443d53b0xd02112ef9c39f1cea7c8527c26242ca1f5d26bcfe8d1564bee054d3b041754719
USDC0xc26c91055de37528492e7e97d91c6f4abe34aae26f2c4d25cff6bfe45b5dc9a90xd02112ef9c39f1cea7c8527c26242ca1f5d26bcfe8d1564bee054d3b041754716
USDe0x86a1beb50c844f5eff9afd21af514a13327c93f76edb89333af862f70040b1070xd02112ef9c39f1cea7c8527c26242ca1f5d26bcfe8d1564bee054d3b041754719
sUSDe0xd2886b34454e2e0de47a82d8e6314b26e1e1312519247e8e2ef137672a909aeb0xd02112ef9c39f1cea7c8527c26242ca1f5d26bcfe8d1564bee054d3b041754719
wstETH0xb42cd9ddf61898da1701adb3a003b0cf4ca6df7b5fe490ec2c295b1ca43b33c80xd02112ef9c39f1cea7c8527c26242ca1f5d26bcfe8d1564bee054d3b041754719

Fuel Mainnet

NameAsset IDContract AddressDecimals
Ethereum0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad079
Fuel0x1d5d97005e41cae2187a895fd8eab0506111e0e2f3331cd3912c15c24e3c1d820x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
WETH0xa38a5a8beeb08d95744bc7f58528073f4052b254def59eba20c99c202b5acaa30x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
weETH0x239ed6e12b7ce4089ee245244e3bf906999a6429c2a9a445a1e1faf56914a4ab0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
rsETH0xbae80f7fb8aa6b90d9b01ef726ec847cc4f59419c4d5f2ea88fec785d1b0e8490x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
rETH0xf3f9a0ed0ce8eac5f89d6b83e41b3848212d5b5f56108c54a205bb228ca30c160x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
wbETH0x7843c74bef935e837f2bcf67b5d64ecb46dd53ff86375530b0caf3699e8ffafe0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
rstETH0x962792286fbc9b1d5860b4551362a12249362c21594c77abf4b3fe2bbe8d977a0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
amphrETH0x05fc623e57bd7bc1258efa8e4f62b05af5471d73df6f2c2dc11ecc81134c4f360x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
Manta mBTC0xaf3111a248ff7a3238cdeea845bb2d43cf3835f1f6b8c9d28360728b55b9ce5b0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
Manta mETH0xafd219f513317b1750783c6581f55530d6cf189a5863fd18bd1b3ffcec1714b40x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
Manta mUSD0x89cb9401e55d49c3269654dd1cdfb0e80e57823a4a7db98ba8fc5953b120fef40x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
pumpBTC0x0aa5eb2bb97ca915288b653a2529355d4dc66de2b37533213f0e4aeee3d3421f0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e88
FBTC0xb5ecb0a1e08e2abbabf624ffea089df933376855f468ade35c6375b00c33996a0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e88
SolvBTC0x1186afea9affb88809c210e13e2330b5258c2cef04bb8fff5eff372b7bd3f40f0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
SolvBTC.BBN0x7a4f087c957d30218223c2baaaa365355c9ca81b6ea49004cfb1590a5399216f0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
Mantle mETH0x642a5db59ec323c2f846d4d4cf3e58d78aff64accf4f8f6455ba0aa3ef000a3b0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
sDAI0x9e46f919fbf978f3cad7cd34cca982d5613af63ff8aab6c379e4faa1795529580x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
USDT0xa0265fb5c32f6e8db3197af3c7eb05c48ae373605b8165b6f4a51c5b0ba4812e0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e86
USDC0x286c479da40dc953bddc3bb4c453b608bba2e0ac483b077bd475174115395e6b0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e86
USDe0xb6133b2ef9f6153eb869125d23dcf20d1e735331b5e41b15a6a7a6cec70e86510x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
sUSDe0xd05563025104fc36496c15c7021ad6b31034b0e89a356f4f818045d1f48808bc0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
rsUSDe0x78d4522ec607f6e8efb66ea49439d1ee48623cf763f9688a8eada025def033d90x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
wstETH0x1a7815cc9f75db5c24a5b0814bfb706bb9fe485333e98254015de8f48f84c67b0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
ezETH0x91b3559edb2619cde8ffb2aa7b3c3be97efd794ea46700db7092abeee62281b00x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
pzETH0x1493d4ec82124de8f9b625682de69dcccda79e882b89a55a8c737b12de67bd680x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
Re7LRT0xf2fc648c23a5db24610a1cf696acc4f0f6d9a7d6028dd9944964ab23f6e359950x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
steakLRT0x4fc8ac9f101df07e2c2dec4a53c8c42c439bdbe5e36ea2d863a61ff60afafc300x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e89
USDF0x33a6d90877f12c7954cca6d65587c25e9214c7bed2231c188981c7114c1bdb789

Verified Contracts

Ethereum Mainnet

Fuel Mainnet

Contract NameContract Address
FuelL2BridgeId0x4ea6ccef1215d9479f1024dff70fc055ca538215d2c8c348beddffd54583d0e8

Ethereum Testnet

Fuel Testnet

Contract NameContract Address
FuelL2BridgeId0xd02112ef9c39f1cea7c8527c26242ca1f5d26bcfe8d1564bee054d3b04175471

Fuel Bridge Security Council

Threshold Requirement: 10/13

Tokenomics

With the introduction of FUEL and its total initial supply of 10 billion tokens, we aim to create a level playing field that ensures fair access for both existing members and newcomers to our growing ecosystem. To acknowledge and reward our long-standing friends, builders, and users, 20% of the initial supply will be distributed to the community, with eligibility determined by factors such as participation in the Fuel Points Program and our incentivized testnet. In total, over 51% of all FUEL tokens will ultimately be allocated to the community, the broader ecosystem, and ongoing research and development efforts, reflecting our commitment to inclusivity and shared growth.

Allocation Pie Chart

GroupDetailsAmount
Community ExpansionUsed for incentives, programs, campaigns and activations for the Fuel community and expansion.2.00 billion
Ecosystem and R&DUsed to establish the sequencing network, and for ecosystem development and Fuel technology R&D.1.55 billion
Ecosystem and R&D 24Same as above. These locked tokens can be staked during the 24 month block-by-block linear release. Staking rewards will only be used for ecosystem development efforts and to enable L2 level incentives.1.55 billion
Contributors 24Includes past and present Fuel contributors.0.60 billion
Contributors 48Core project contributors.0.98 billion
PurchasersToken purchasers from 2020 to 2022.3.31 billion

Inflation

Inflation will be 3% annually. Note that inflation and Fuel economics are configured by the sequencer validator set.

Unlocks

Note: Team and Purchasers can't stake locked tokens.

Release Schedule

GroupDetailsVesting Schedule
CommunityUsed for incentives, programs, campaigns and activations for the Fuel community and expansion.Immediate
Ecosystem and R&DUsed to establish the sequencing network, and for ecosystem development and Fuel technology R&D.Immediate
Ecosystem and R&D 24Same as above. These locked tokens can be staked during the 24 month block-by-block linear release. Staking rewards will only be used for ecosystem development efforts and to enable L2 level incentives.24 month linear release
Contributors 24Includes past and present Fuel contributors.24 month linear release
Contributors 48Core project contributors.48 month linear release
PurchasersToken purchasers from 2020 to 2022.24 month linear release

How to Upgrade Fuel V1 to FUEL

The Fuel V1 token was the previous iteration of the FUEL token. The initial supply of Fuel V1 tokens was 100 million. The supply of the current FUEL token is 10 billion. Fuel V1 tokens can accordingly be upgraded at a ratio of 1:100.

Please visit app.fuel.network/upgrade to upgrade your Fuel V1 tokens to FUEL tokens. Below is a short help guide:

Connecting an EVM Wallet

Fuel V1 grants exist only on the Ethereum mainnet. Please connect an EVM wallet with a token grant or Fuel V1 tokens using the button in the top-right corner.

Connect EVM Wallet

Claiming a Token Grant

If you have a token grant, the amount of unclaimed tokens will be displayed, along with a button to claim the tokens into your wallet. If you already have Fuel V1 tokens ready to upgrade, you can skip this step.

Claim Token Grant

Upgrade Fuel V1 Tokens to Fuel Tokens

Once you have Fuel V1 tokens in your wallet, click the "Upgrade to FUEL" button to start the upgrading process. This will take you to two screens.

Upgrade To FUEL

The first screen is to approve the amount of tokens to be upgraded.

Approve Amount

The second screen is to complete upgrading of Fuel V1 tokens to FUEL tokens.

Confirm Conversion

That's it! You can now deposit and delegate your tokens to start earning rewards. Since your tokens are on the Ethereum mainnet, you have two options:

  1. Stake on Ethereum:

    Use app.fuel.network/staking/on-ethereum to stake your tokens on the shared sequencer network from Ethereum. Note that this interface differs from the one used for tokens on Fuel Ignition. Follow the instructions provided in How to Stake on Mainnet Ethereum.

  2. Bridge and Stake on Fuel Ignition:

    Alternatively, you can withdraw and bridge your tokens to Fuel Ignition to benefit from lower costs and higher speeds. Use the Fuel Bridge to transfer your tokens, then stake them using app.fuel.network/staking/on-fuel. Follow the instructions provided in the How to Stake on Fuel Ignition guide.

Balance

Note: It may take some time for your balance to appear on the shared sequencer network. Please don’t panic—your balance will update soon.

Claim Genesis Drop

Please visit app.fuel.network/earn-points/drop/ to claim your FUEL airdrop. Below is a brief help guide to assist you.

Check Eligibility

Start by connecting all accounts eligible for Phase 1 of the FUEL airdrop. This includes GitHub accounts, Fuel wallets (both native and non-native), and Ethereum wallets.

Connect Accounts

You connect all three wallet types at the same time and view the claimable amount for each individual account.

Airdrop Amount Multi

Note that any eligible Ethereum accounts you connect will automatically load their corresponding eligible Fuel accounts. If rewards were earned using a non-Fuel wallet, they will still appear in the Fuel Wallet where the tokens were bridged.

Airdrop Amount Pt2

Claim

To start the claiming process, click “Join the Claim Queue.”

Join Queue

Once you have joined the queue, you will have exactly 10 minutes to claim your FUEL tokens from each of your eligible accounts or else you will have to requeue.

Claim

Please carefully read over the terms of service before proceeding to claim your tokens. Once you’re ready, simply click “Claim.”

Claim Pt2

When claiming through an Ethereum wallet, you can connect a Fuel wallet to receive your tokens. Plus, as a bonus, if you don't have any ETH on Fuel Ignition, you’ll get a small amount of gas to help you claim your Fuel tokens!

Claim Eth

Congratulations and thank you for being a part of the Fuel community! Now, please head over to Fuel Ignition and follow this guide to stake your tokens.

How to Stake on Fuel

Please visit app.fuel.network/staking/on-fuel to stake your tokens on the Fuel Shared Sequencer Network through Fuel Ignition. Below, you'll find a helpful guide to get started.

Connect Fuel Wallet

Start by connecting a Fuel wallet with FUEL tokens. This includes both non-native EVM and SVM wallets.

Connect EVM Wallet
Connect EVM Wallet 1.5

Stake

From left to right, you can view your FUEL token balance in your Fuel wallet, your staked FUEL token balance on the shared sequencer network, and your accumulated rewards.

Press the "Stake" button next to your balance to begin the staking process.

Stake Tokens

Enter the number of tokens you wish to delegate to the validators. Then, click the "Deposit" button to stake your tokens.

Please note: unlike staking on mainnet Ethereum, the validator delegation process is automatically assigned, so you don’t need to manage each validator manually.

Stake Tokens

After successfully depositing, your total staked token balance will update. The dashboard below will display the distribution of your delegated tokens. In the example diagram, the tokens are distributed evenly among the validators.

Validator Distribution

Withdraw

Withdrawing from the shared sequencer network is as simple as staking. Click the "Withdraw" button beside the amount of tokens you have staked.

Withdrawal

Select the amount of FUEL tokens you wish to withdraw, then click the "Withdraw" button.

Withdrawal Pt2

Your undelegated token balance will return to your Fuel wallet, and the validator dashboard will update accordingly as well.

Withdrawal Pt3

Claim Rewards

Once you have staked your FUEL tokens, you will start to accumulate FUEL rewards. Click "Claim" next to your earned rewards balance to collect your tokens.

Claim Rewards

How to Stake on Mainnet Ethereum

Please visit app.fuel.network/staking/on-ethereum to stake your tokens on the Fuel Shared Sequencer Network from Ethereum. Below is a help guide:

Connect EVM Wallet

Start by connecting an Ethereum wallet with FUEL tokens, whether they are already in the shared sequencer network or in your mainnet wallet.

Connect EVM Wallet

Stake

From left to right, you can see your balance of FUEL tokens in your Ethereum wallet, your balance of FUEL tokens in the shared sequencer network, and the rewards accumulated.

Please note that tokens still in your mainnet Ethereum wallet must be migrated to the shared sequencer network first before you can delegate and stake. Don’t worry, as this will follow the same flow.

Token Balances

Select Validator

Scroll down to the bottom of the page. There, you will see a list of validators. From the list, choose the validator to which you want to stake and delegate your FUEL tokens. Click the "Deposit" button.

Select Validator

Delegating Tokens

Select the amount of FUEL tokens you want to delegate, and approve those tokens.

Delegate Tokens

Once approved, confirm your delegation.

Open Positions

After the tokens have been successfully delegated and staked, you will see your tokens under "Delegated Positions."

Open Positions

Claim Rewards

After staking your FUEL tokens, you will immediately start to accumulate FUEL rewards, which you can claim. Click "Claim" on any of your open delegated positions to collect your rewards.

Claim Rewards

Your rewards will be added to your balance in the Fuel shared sequencer network.

Claim Rewards Pt2

Withdrawal

You can undelegate and withdraw your staked FUEL tokens at any time. However, there is a standard 14-day unbonding period before the undelegation is completed, ensuring economic security within the PoS shared sequencer network and the Ethereum mainnet. After the 14-day period, the withdrawal process requires 4096 sequencer blocks approximately seven more hours before your tokens become available in your mainnet Ethereum wallet.

Undelegate

To undelegate your open positions, simply press the hamburger button beside your position and click the "Undelegate" button.

Withdrawal

Here, you can select how many delegated tokens you wish to undelegate. These tokens will return to your balance in the shared sequencer network.

Withdrawal Pt2

Withdraw Balance in Sequencer

Lastly, you can withdraw your tokens from the shared sequencer network back to your mainnet Ethereum wallet by clicking the "Withdraw" button beside your balance in the shared sequencer.

Withdrawal

Here you can specify how many tokens you want to withdraw.

Withdrawal Pt2

Chapter 1 - Why Fuel?

Beginnings

A multitude of fragmented solutions—Layer 1s, rollups, DA layers, sequencers—clutter the blockchain landscape today, each striving to scale, decentralize, and power the internet's future. However, execution inefficiencies, unsustainable growth, and security compromises hamper the current array of solutions.

In the race to market, numerous solutions opted to copy and paste existing architectures. Few dared to build from the ground up. Why? Most projects focused on sustaining innovations, making incremental improvements to older frameworks. We took the opposite approach and aimed for disruptive innovation, challenging the status quo with a completely new architectural vision to overcome the limitations others had accepted.

Picture a group of early Ethereum developers, driven by a vision to enhance the performance, sustainability, interoperability, and developer experience of Ethereum. Recognizing the need for a fresh architectural approach to achieve ambitious goals, these devs envisioned a system incorporating years of blockchain evolution while adhering to cypherpunk ideals of decentralization and accessibility.

The blockchain ecosystem has undergone a remarkable transformation since the inception of Bitcoin in 2009. Bitcoin and Ethereum, as pioneering Layer 1 blockchains, established the foundation for decentralized systems, but quickly encountered scalability challenges. In response, alternative L1s offered differing approaches to decentralization, security, and performance. As demand for scalability grew, Ethereum explored modular approaches and various solutions like state channels, plasma, and eventually rollups—Layer 2 solutions that aggregate transactions to improve throughput while leveraging the security of L1s.

Sequencers, integral to rollups, emerged to manage transaction ordering and boost efficiency, forming a critical piece in the evolving blockchain landscape. This wave of innovation also sparked developments in Proposer-Builder Separation (PBS) and other modular solutions that allowed for specialization at various layers of the blockchain stack—execution, settlement, data availability, and consensus—pushing the boundaries of what these networks could achieve.

Despite such advancements, the blockchain landscape still lacks a crucial piece of the puzzle: scalability without compromise. Most solutions sacrifice decentralization for performance, or security for speed, resulting in trade-offs that undermine the core principles of blockchain technology.

Many L1 and L2 solutions boost transaction capacity by increasing node requirements, thus enhancing throughput and cutting latency. This approach, however, shrinks the pool of participants capable of validating and securing the chain.

Similarly, some rollups and sidechains achieve higher speeds by implementing trust assumptions that deviate from the foundational principles of security and decentralization. These solutions may rely on multi-signature schemes or other trust-based models to validate transactions, which introduce vulnerabilities. Users must place their trust in small groups of signers, which can be susceptible to hacking or coordination attacks.

This critical need for a scalable, trustless, and performant system—one that doesn’t trade off on the core principles of blockchain—remains unmet.

We built Fuel to address this critical gap.

In December 2020, Fuel emerged as the first optimistic rollup on Ethereum, with the launch of Fuel V1. We sought to create a trust-minimized sidechain that would inherit the security of Ethereum while introducing a radically redesigned execution model based on UTXOs, or Unspent Transaction Outputs.

Fuel V1 garnered significant attention within the blockchain community from day one. Many regarded Fuel V1 as the one “pure” rollup, primarily due to its approach to security and execution. Unlike other architectures, Fuel V1 demonstrated security inheritance without relying on third-party multi-signatures or sacrificing the integrity of optimistic fraud proofs.

Vitalik's appreciation for Fuel.

Fuel V1’s design philosophy set the bar for Ethereum rollups and ultimately, our vision, leading to a more refined architecture in Fuel V2.

Over the past three and a half years, Fuel has evolved significantly, morphing into a new blockchain architecture that thoughtfully addresses the common challenges faced by modern blockchains. Our vision culminated in what we now call Fuel V2, an operating system for rollups—the "Rollup OS." This framework empowers developers to build and customize their own rollups while leveraging the security and robustness of underlying L1s like Ethereum for settlement and access to Ethereum’s vast liquidity and assets.

Imagine Fuel as a robust framework designed to foster the development of sustainable and high-performance rollups, along with novel, advanced applications never before seen in blockchain. By providing this architecture, we empower developers to build innovative, decentralized solutions that push the boundaries of what's possible in the ecosystem.

We envision every application eventually evolving into its own app-chain, with Fuel providing the optimal architecture, tools, and developer experience for that future. Our commitment extends to creating pathways for the community to support millions of innovative app-chains, establishing Fuel as the foundation for the next generation of decentralized applications.

Fuel's narrative interweaves with Bitcoin and Ethereum's histories. Bitcoin's concise yet revolutionary whitepaper sparked a philosophical movement centered on self-sovereignty and cryptographic trust. Ethereum then expanded the horizon, introducing a programmable platform that unleashed developers' creativity and innovation.

Fuel acknowledges these contributions while seeking to fill the gaps left by conventional architectures. As we delve deeper into the intricacies of blockchain technology, we invite you to explore the problems we aim to solve and the vision we aspire to realize. Welcome to the beginnings of Fuel—a journey toward a sustainable, performant, and decentralized future.

The Problem

The blockchain landscape has advanced rapidly, but key issues still limit decentralized technology’s potential. Fuel is designed to address these fundamental challenges head-on. To understand its significance, we must first examine the core problems that blockchains face today.

The Performance Bottleneck

Performance can be measured in various ways, such as speed to finality, total execution load capacity, transactions per second and cost-efficiency. However, traditional networks like Ethereum struggle to scale efficiently across these aspects.

One of the critical issues is load. Ethereum's computation overhead limits its processing capacity, restricting the number of transactions per second (TPS), compute units per gas unit, and overall execution capacity. Proving cryptographic evidence of transactions consumes much of this computation. The EVM, for instance, bears a significant cryptographic burden as it verifies and updates the state tree after every transaction. Ethereum's execution layer compounds this inefficiency by poorly optimizing for the underlying hardware, creating unnecessary computational overhead.

Sequential execution of these computations further constraints throughput.If you remove the state tree and focus solely on EVM computation, Ethereum could potentially achieve up to 10,000 TPS. However, the real bottleneck comes from the execution side and the cryptographic evidence required for state verification. Fuel addresses this by removing the need for a state tree and enabling computations to run in parallel, dramatically increasing efficiency. Fuel not only makes execution more efficient but also allows it to scale horizontally, making the system far more accessible to users without driving up costs or limiting throughput.

Cost-efficiency remains a problem. Ethereum's transaction costs fluctuate often, spiking to inaccessible heights during congestion. This unpredictability harms both developers and users, constraining the types of applications they can scale.

The Scalability Dilemma

Many blockchain projects attempt to achieve scalability by increasing the hardware requirements for their nodes. This approach often makes it more expensive for users to participate, sidelining smaller users and compromising decentralization. Scalability demands more than incremental improvements. It requires fundamental changes to transaction and block processing that enhance efficiency without escalating the network's computational burden.

Early blockchains like Ethereum process transactions sequentially, executing one after another. This linear model severely limits performance on modern multi-core processors designed for parallel processing.

Parallelism, or the ability to process multiple transactions simultaneously, is one of the most promising solutions for blockchain scalability. However, enabling parallel transaction execution requires careful management of state access. If two transactions try to access the same state (for instance, attempting to spend the same funds), they can’t be processed in parallel. This leads to complex mechanisms in many blockchains that either attempt to predict state conflicts or reprocess conflicting transactions.

Fuel addresses these issues by adopting the UTXO model instead of Ethereum’s account-based model. In this model, every transaction defines its own state in the form of unspent transaction outputs, eliminating the risk of conflicting state access. As a result, Fuel can safely parallelize transaction execution, dramatically increasing throughput without compromising security. For developers, this complexity is abstracted away, allowing them to build seamlessly without needing to manage UTXOs directly.

Fuel's architecture introduces stateless primitives called predicates, enhancing efficiency and simplifying state management.This statelessness allows predicates to be trivially processed in parallel, enabling a high degree of concurrency in transaction execution. Predicates facilitate the execution of multiple operations within a single transaction while ensuring that conflicts with other transactions are avoided. Predicates don’t maintain state information between executions, enabling efficient parallel processing and significantly boosting throughput. Fuel's unique architecture enables critical performance gains, powering scalable real-world decentralized applications.

State Growth and Sustainability

Blockchain growth inflates state size. State encompasses a blockchain's stored data: account balances, smart contract bytecode, and dApp interactions. Unchecked state growth explodes exponentially, threatening system stability. Every new transaction adds more data, and this accumulation increases the burden on node operators. As the state becomes larger, nodes must store and manage increasingly extensive amounts of data, leading to higher hardware requirements and potentially threatening decentralization.

Ethereum is currently grappling with state bloat, which many core developers consider the network’s most pressing scaling issues. As the state grows, nodes must store increasingly large amounts of data, which increases hardware requirements. The ecosystem continually explores possible solutions, such as statelessness and state expiry, but has yet to fully implement any. Ethereum's backwards compatibility limits radical innovation, while Fuel's flexibility enables more flexibility and scalable solutions.

Fuel directly addresses state growth by minimizing unnecessary data accumulation. It discourages excessive state use with op code pricing, pushing developers to optimize their applications. By streamlining data storage and management, Fuel reduces the state nodes must maintain, easing the load on operators. Its architecture efficiently handles data, ensuring the state remains manageable as transactions are processed. Our approach preserves decentralization and accessibility, allowing the network to scale without encountering the challenges of unchecked state growth.

Interoperability and Fragmentation

Another major problem in today’s multi-chain world is interoperability. Ethereum's unified state machine succeeds largely by enabling application composition, universal asset access, and seamless dApp interactions. However, Ethereum's congestion has driven users to migrate to other L1s and L2s, fragmenting the ecosystem. Each new chain comes with its own set of challenges, including the need for separate wallets, token bridges, and onboarding processes.

Fuel is designed to reunite the fragmented ecosystem with a focus on interoperability at its core. Despite concerns about short-term fragmentation, Fuel's new VM and toolset aim to reduce ecosystem division long-term. Unlike most rollup projects, Fuel is not constrained by the EVM. Its flexible architecture enables innovative design choices for seamless interactions across multiple chains while maintaining full Ethereum compatibility. Fuel’s transaction model and block design simplify cross-chain integrations, making the movement of assets and data between chains easier to manage.

Fuel's proposed shared sequencer design prioritizes speed and efficiency, rapidly processing cross-chain transactions. Our fast sequencing empowers developers to build versatile, low-latency cross-chain applications, mitigating typical multi-chain fragmentation.

Fuel’s unique transaction and block architecture further enhances interoperability by providing execution evidence in the form of receipt roots and smart contract state roots. Verifiable proofs facilitate inter-chain interactions with Fuel, enhancing user experience and cross-chain fluidity.

The Future: A Modular, Decentralized World

Performance limitations, poor scalability, unsustainable state growth, and minimal interoperability plague the current blockchain landscape. These issues threaten blockchain decentralization and constrain thriving applications. Fuel's innovations—UTXO-based parallelism, modular architecture, and cross-chain capabilities—overcome these limitations, setting a new blockchain infrastructure standard.

The Fuel Way

Blockchains have largely followed Ethereum's evolutionary path since its launch.

Subsequent chains tout increased speed, scalability, power, and usability. They implement novel consensus mechanisms, databases, and ZK proving systems.

Despite these innovations, the core system remains largely unchanged: developers craft smart contracts for applications and assets (typically in Solidity or Rust). Users rely on centralized servers to read on-chain data and interact by signing messages with a standard private key, then routing these signed messages back through the same centralized servers.

Fuel charts a new course for the blockchain industry, prioritizing decentralization at its core. We're not just iterating; we're rebuilding blockchain architecture from the ground up.

Decentralized… Sustainably Decentralized

Blockchains fundamentally consist of a network of distributed nodes, all validating new blocks and transactions. The ability for independent, distributed and unqualified actors to participate in this process is what gives blockchains their valuable properties of liveness, censorship resistance and verifiability.

Bitcoin continues to take the most principled stance on maintaining these properties. The low node requirements and low bandwidth usage mean that Bitcoin full nodes can be run on devices as light as Raspberry Pis, and in locations as remote as outer space.

However, subsequent blockchains have all made ongoing compromises. Most newer blockchains today (including most layer-2s) can only be run on high-powered servers with data-center connections. And some high throughput projects remove the key cryptographic primitives of verifiability, such as the merkelization of state elements.

Fuel aims to pull the blockchain space back from this creeping centralization, back towards the values of Bitcoin. The Fuel architecture allows for high performance, while still running on consumer hardware. Fuel always maintains the property of cryptographic verifiability, allowing users to check the state of the chain without trusting third parties.

Blockchains are not Computers

Advancing blockchain technology demands more than incremental upgrades. True innovation often requires revolutionary action– including breaking changes. Fuel envisions revolutionizing both blockchain architecture and application development to unlock the technology's full potential.Traditional smart-contract platforms mimic computer systems, with blockchains serving as hardware and smart contracts as software. These contracts execute read and write operations, storing data to the chain's state—effectively treating it as a global Postgres database.

Fuel believes that blockchains are not simply scaled-up abstract mainframes but a different kind of computer—"trust machines." These machines are still programmable, but they operate under vastly different constraints than traditional execution environments. The role of a blockchain node is not to act as a cloud server but to verify the current state of the chain and all future state transitions with trustless integrity.

Moving computation off blockchain full nodes and shifting data outside of the blockchain’s state keep full node requirements low, allowing blockchains to scale without centralizing. Fuel enables developers to build smart applications without smart contracts, simplifying development while maintaining the decentralized ethos of blockchain technology.

ZK Pragmatism

Zero-knowledge technology has captured the imagination of researchers and developers from across the blockchain industry. The promise of succinct verification for arbitrary computation has opened up a whole new range of possibilities for scaling blockchains, making them verifiable, interoperable, and more. The thesis of building the future of ZK-powered blockchain tech has driven some of the most anticipated and well-funded projects in this space.

Fuel adopts a pragmatic approach to zero-knowledge (ZK) technology while recognizing its groundbreaking potential within and beyond blockchains. We share the industry's excitement about these new primitives and are actively integrating ZK technology into the Fuel stack (such as in Fuel’s hybrid-proving model and with the service chain’s ZK-powered bridge).

Fuel asserts that blockchain security, high performance, and interoperability should not hinge on ZK technology alone. Fuel pioneered the first optimistic rollup on Ethereum, diverging from the prevalent focus on ZK rollups among Ethereum scaling solutions. Fuel maintains that full ZK-verification cannot sustainably meet the market's stringent cost and performance demands.Proof generation costs and time constraints render fully ZK-proven chains incompatible with both cost-effectiveness and high-speed operations. Sustainable proofs and 'real-time proving' typically rely on ZK-specific hardware, which faces numerous production-readiness hurdles.

Fuel crafts cutting-edge blockchain technology, selectively integrating off-the-shelf ZK solutions to enhance its stack. The rise of generalized ZK-VMs like RISC Zero and Succinct’s SP-1 point to a future where ZK technology is commodified and easily available without the need for directly handling the necessary cryptography.

So What is Fuel?

Fuel is a next-generation execution layer for Ethereum, designed to offer unparalleled speed, flexibility, and scalability for both developers and users. But what exactly sets Fuel apart from other solutions? And why should you, as a developer, invest your time and energy into learning this architecture?

Fuel embodies a core philosophy of modularity and performance. For developers, Fuel’s appeal lies in both its design philosophy and the tools it offers. Here are a few key reasons why developers should pay attention to Fuel:

  1. Unmatched Parallelization: Fuel’s unmatched parallelization enables simultaneous transaction processing, allowing significantly higher throughput than many other blockchains. By eliminating serial processing bottlenecks, developers can build scalable, efficient dApps without sacrificing performance. What truly sets Fuel apart is the introduction of predicates, stateless smart accounts that allow transactions to execute in parallel without conflict—something other blockchains struggle to achieve. Combined with the UTXO (Unspent Transaction Output) model, this ensures seamless, concurrent transaction execution, driving scalability to new heights.

  2. Native Assets: Fuel natively supports a wide variety of assets, but its unique architecture handles them more efficiently. Unlike blockchains that handle only one native asset (like Ethereum's focus on ETH), Fuel enshrines all assets at the protocol level. This means developers don't need to create custom smart contracts for simple asset operations like transfers or balance checks. These operations are built into the system, significantly reducing complexity, time, and the risk of introducing vulnerabilities. Native assets also benefit from performance optimizations, avoiding the overhead of virtual machine processing.

  3. Security and Safety: Fuel’s architecture eliminates many of the common vulnerabilities seen in smart contract platforms, such as reentrancy attacks. By integrating asset logic into the protocol itself, developers no longer have to rely on third-party contracts or code that could introduce risks. When you transfer tokens or execute other asset-related functions on Fuel, you do so with the assurance that the execution won’t be hijacked by malicious code, significantly enhancing security.

  4. Developer-Friendly Tooling: Fuel offers an integrated, developer-friendly environment that makes building on its platform easier. Whether you're a seasoned blockchain developer or new to the space, Fuel provides a robust set of tools to support your development process. The Sway programming language, specifically designed for FuelVM, ensures that you’re writing optimized and secure smart contracts. In addition, Fuel’s native development kits (like Forc, the Fuel SDK, and Wallet SDK) make it easy to deploy, test, and manage decentralized applications.

  5. Future-Proof Design: The blockchain space evolves rapidly, but Fuel is built to grow alongside it. Its modular design allows for the easy adoption of future innovations, whether in virtual machine improvements, consensus upgrades, data availability solutions or ZK. This flexibility ensures that Fuel can adapt to whatever comes next without developers having to constantly rework their applications.

Building on Fuel: An Overview

Building on Fuel empowers developers to create high-performance, scalable decentralized applications with cutting-edge tools and infrastructure. Fuel’s architecture prioritizes speed, security, and developer productivity. This section outlines the core components of the Fuel ecosystem. We will explore each component in further detail in Part 2.

The FuelVM (Fuel Virtual Machine)

The FuelVM incorporates years of blockchain design to bring the Ethereum community a reliable machine architecture built for longevity. It drives the Fuel Network and delivers exceptional performance by processing transactions in parallel. Unlike most blockchain virtual machines like the Ethereum Virtual Machine (EVM), which execute transactions serially, FuelVM handles concurrent processing, dramatically increasing throughput.

The FuelVM draws on a variety of architectures, including RISC-V, ARM ISAs, Bitcoin scripts, and the Ethereum Virtual Machine, to create a low-level execution environment optimized for blockchain use cases. By offering state-minimized facilities like native assets, ephemeral scripting, and spending conditions, it reduces the load on full nodes, improving network sustainability. Developers can avoid the inefficiencies of traditional state-heavy designs and build applications that deliver high performance while keeping the network decentralized and accessible.

As of May 2024, the FuelVM can achieve asset transfer benchmarks of 21,000 transactions per second (TPS) per core on high-end CPUs, offering unparalleled speed for modern blockchain applications.

The Fuel Transaction Architecture

Fuel’s transaction architecture brings together lessons from Bitcoin, Ethereum, Cosmos, and Solana to create a highly parallel and efficient transaction model. By using a UTXO (Unspent Transaction Output) model, Fuel enables parallel execution both within and across blocks, allowing developers to process transactions quickly without overloading the network.

Fuel transactions are flexible enough to handle everything from simple asset transfers to complex multi-party, multi-asset interactions and batch smart-contract calls. Developers can build sophisticated applications using advanced conditional logic with predicates, reducing the need for state-heavy smart contracts. By minimizing reliance on state, developers can ensure that applications perform efficiently without overburdening network resources.

Fuel’s transaction model also solves concurrency issues seen in other UTXO-based blockchains. This maintains a familiar developer experience for those coming from Ethereum while benefiting from the performance advantages of UTXO-based execution.

Fuel Ignition (Rollup)

Fuel Ignition will be the first Fuel V2 rollup to go live on Ethereum Mainnet. It aims to surpass traditional EVM rollups by delivering a vastly improved execution design. Initially starting as a more trustful Layer-2, Ignition’s ultimate goal is to evolve into a fully Ethereum-secured rollup with fraud proving, decentralized sequencing, and secure upgrades via a delayed multi-signature process.

Ignition’s focus on leveraging Ethereum’s security ensures that developers can build high-performance applications while benefiting from the strong security guarantees Ethereum offers. As Ignition develops, it will incorporate decentralized sequencing and Ethereum-based data availability (DA), further enhancing its trustless, scalable design.

The Fuel Network

Fuel operates as a network of interconnected rollups, designed to offer seamless interaction between different blockchains and rollups. Fuel rollups diverge from the copy-paste approach common in many rollup networks. Fuel's customizable VM configurations enable tailoring each network blockchain to developers' specific needs, enhancing adaptability across diverse use cases. Combined with its decentralized block production model, enabled by a shared sequencing and builder network, Fuel provides a fair and efficient system for managing transaction inclusion and interoperation between rollups.

Developer Tooling

The Fuel project realized early on the importance of thoughtful and considerate developer tooling. We consider developer time one of our community's most important assets and aim to optimize it for building high-value code. To maximize developer productivity and enable the creation of future-proof applications, we created our own suite of tools. These tools streamline building, testing, and deploying decentralized applications, freeing developers to focus on innovation.

Sway: Sway is a domain specific language (DSL) for modern blockchain programming which has familiar syntax, grammar and design ideology to Rust while incorporating blockchain specific functionality such as smart contract interface concepts. Sway is inherently designed to save developers time by providing a single programming language for constructing all critical blockchain application components such as: predicates, scripts, smart contracts, libraries, testing, deployment scripting, indexing and more.

Why not Rust or Solidity? Rust, primarily designed as a systems language, heavily bonds to the Low Level Virtual Machine (LLVM) toolchain and lacks focus on the special considerations of blockchain development. Solidity, a powerful language for developing on the Ethereum Virtual Machine, has many known shortcomings. Sway aims to combine the best aspects of both languages, offering developers a familiar yet powerful tool for blockchain development.

Other tools include:

  • Forc (Fuel Orchestrator): This command-line toolchain serves as the backbone of Fuel development. It supports everything from compiling Sway smart contracts to managing dependencies and deploying applications. Forc simplifies the entire development process, ensuring that developers can build robust dApps with ease.

  • Fuel Rust SDK: The Rust SDK allows developers to interact with Fuel’s blockchain using the Rust programming language. It offers a seamless experience for creating system-level applications and managing interactions with the Fuel Network.

  • Fuel Wallet SDK: The Fuel Wallet SDK provides developers with the tools to create secure, user-friendly wallets that natively interact with the Fuel ecosystem. It ensures developers can easily build wallets that integrate into decentralized applications.

  • Fuel Typescript SDK: The Typescript SDK allows developers to integrate Fuel into web applications, simplifying interaction with the Fuel blockchain and enabling frontend developers to build decentralized applications that connect with Fuel’s infrastructure.

Chapter 2 - The Architecture

The FuelVM

Highlights:

  • The FuelVM serves as the core of the Fuel stack, informed by insights from various virtual machine designs like the EVM and Solana's SVM.

  • It supports state-minimized application development through features such as native assets and ephemeral scripting, promoting decentralized and accessible architecture.

  • The UTXO model facilitates parallelized transaction execution, enhancing throughput and reducing latency by allowing concurrent processing of non-conflicting transactions. The FuelVM is a register-based virtual machine, providing better performance compared to traditional stack-based designs and offering a structured instruction set for efficient operations.

  • Predicates, scripts, and contracts are essential components of the FuelVM, enabling flexible spending conditions, transaction processing, and state management within its execution environments.

The FuelVM lies at the core of the whole Fuel stack; it was created by taking into account years of learning from other virtual machine designs, like the EVM, Solana’s SVM and more.

The FuelVM enables developers to move away from stateful application designs often enabled by smart contracts by providing more feature-rich state-minimized facilities such as native assets, ephemeral scripting, and ephemeral spending conditions. By offering alternative methods for developers to build state-minimized applications, we enhance full node sustainability while maintaining a decentralized and accessible architecture, aligning with Ethereum’s core values.

In the following sections, we will discuss the key features of the FuelVM in detail.

UTXO Model and Parallelization

Fuel’s parallelized transaction execution model serves as a cornerstone for its efficiency and scalability. Parallelization dramatically improves throughput and reduces latency compared to traditional sequential processing methods. It breaks tasks into smaller sub-tasks, executing them simultaneously across multiple processing units.

Parallelization is built upon a foundation of Access Lists and the UTXO (Unspent Transaction Output) model, which works in tandem to enable concurrent processing of non-conflicting transactions.

Our tech leverages the UTXO model for performing transactions on Fuel. Transactions modeled through UTXOs handle everything from token transfers to smart contract calls.

Addresses on Fuel own unspent coins, allowing them to spend and perform transactions through the FuelVM.

2.1 UTXO Model and Parallelization

Using the UTXO model helps achieve transaction parallelization. At runtime, users provide the inputs and outputs for their transaction. Transactions without overlap process in parallel, enabling Fuel to scale horizontally with the number of cores per machine.

Register based design

The FuelVM operates as a register-based virtual machine, unlike the EVM and many others, which use a stack-based architecture.

Register-based virtual machines consistently outperform stack-based virtual machines.

The FuelVM includes 64 registers, each 8 bytes, with 16 reserved and 6-bit addressable.

valueregisternamedescription
0x00$zerozeroContains zero (0), for convenience.
0x01$oneoneContains one (1), for convenience.
0x02$ofoverflowContains overflow/underflow of addition, subtraction, and multiplication.
0x03$pcprogram counterThe program counter. Memory address of the current instruction.
0x04$sspstack start pointerMemory address of bottom of current writable stack area.
0x05$spstack pointerMemory address on top of current writable stack area (points to free memory).
0x06$fpframe pointerMemory address of beginning of current call frame.
0x07$hpheap pointerMemory address below the current bottom of the heap (points to used/OOB memory).
0x08$errerrorError codes for particular operations.
0x09$ggasglobal gasRemaining gas globally.
0x0A$cgascontext gasRemaining gas in the context.
0x0B$balbalanceReceived balance for this context.
0x0C$isinstructions startPointer to the start of the currently-executing code.
0x0D$retreturn valueReturn value or pointer.
0x0E$retlreturn lengthReturn value length in bytes.
0x0F$flagflagsFlags register.

The FuelVM Instruction Set

The FuelVM instructions are 4 bytes wide and have the following structure:

  • Opcode: 8 bits
  • Register Identifier: 6 bits
  • Immediate value: 12, 18, or 24 bits, depending on the operation.

The FuelVM instruction set has been documented in detail here: https://docs.fuel.network/docs/specs/fuel-vm/instruction-set.

Memory

FuelVM uses byte-indexed memory, configurable with the VM_MAX_RAM parameter. Hence, each instance of FuelVM can decide how much memory it wants to allocate for the VM.

Memory follows a stack and heap model. The stack begins from the left, immediately after the initialized VM data and call frame in a call context, while the heap starts at the byte indexed by VM_MAX_RAM.

Each byte allocation on the Stack increases the stack index by 1, and each byte allocation on the heap decreases its writable index by 1. Hence, the stack grows upwards, and the heap grows downwards.

2.1 FuelVM Memory

The stack and the heap have the following essential registers associated with them:

  • $ssp ( 0x05 ): Memory address of bottom of the current writable stack area.
  • $sp ( 0x06 ): Memory address on top of current writable stack area (points to free memory).
  • $hp ( 0x07 ): Memory address below the current bottom of the heap (points to used/OOB memory).

The FuelVM has ownership checks to ensure that contexts have a defined sense of ownership over particular regions in the memory and can only access memory from the region they own. We will elaborate more on this topic in later sections.

Predicates, Scripts and Contracts

Understanding further concepts for Fuel requires grasping:

  • Predicates
  • Scripts
  • Contracts

Let’s dive deeper.

Predicates

Predicates are stateless programs that define spending conditions for native assets. Native assets go to predicates, and to determine whether they are spendable in a transaction, the FuelVM executes the bytecode and checks the boolean return value. If the returned value is true, the asset can be spent; if the returned value is false, then the transaction is invalid!

People can program various spending conditions, such as spending only if three out of five approve a transaction or if a transaction includes specific desired inputs and outputs, commonly known as intents.

Predicates operate statelessly, without persistent storage, and cannot call other smart contracts.

Scripts

Scripts serve as the entry point for Fuel transactions, dictating the flow of the transaction. Like predicates, they lack persistent storage. However, they can call contract Inputs, which are part of the Fuel transaction and can have persistent storage of their own.

This enables Fuel to natively support advanced features such as multi-calls, conditional contract execution, and more.

Contracts

Fuel provides support for smart contracts in its UTXO model. Smart contracts are stateful and can be called by other contracts. In Fuel, smart contracts are represented by the InputContract type. To learn more, refer to the section on InputContract.

The first call to a contract in a transaction occurs through a script, after which the contract can call other contracts.

Contracts have persistent storage, a key-value pair of 32-byte and 32-byte values. Various data structures are being considered to determine the optimal approach for committing to contract storage.

Contexts

A context is a way to isolate the execution of various execution environments for predicate estimation and verification, scripts, and contracts. Each context has its memory ownership, which we will expand on later.

There are four types of contexts in Fuel:

  • Predicate Estimation
  • Predicate Verification
  • Script Execution
  • Calls

The first three are called External contexts, as the $fp is zero, while Calls are called Internal contexts and will have a non-zero value for $fp.

Predicate Estimation

Fuel transactions provide predicateGasUsed for each predicate used. During verification, if predicateGasUsed is less than the total gas consumed during verification, the transaction reverts.

2.1 Predicate Estimation

The user performs Predictive Estimation locally or by calling a remote full node, which executes the predicate in the FuelVM and returns the total gas consumed.

Predicate estimation context cannot do persistent storage or make calls to Contracts.

Predicate Verification

All predicate parts of the transaction are verified to return true before executing the transaction script. The FuelVM is used in the Predicate Verification context when verifying a transaction's predicates.

2.1 Predicate Verification

Predicate verification context cannot do persistent storage or make calls to contracts.

Script Execution

After verifying all predicates, the transaction script is executed; the script execution context cannot do persistent storage but can make calls to contract.

2.1 Script Execution

Calls

Call contexts execute contracts, offering flexibility, storing data persistently, and making contract calls.

Call context is created by either:

  1. Script calling a smart contract
  2. Contract calling a contract input

2.1 Call Context

Each call creates a “Call Frame”, which is pushed to the Stack. A call frame holds metadata on the stack, aiding the execution of the call context in the FuelVM. A call context cannot mutate the caller's state and only access its stack and heap.

bytestypevaluedescription
Unwritable area begins.
32byte[32]toContract ID for this call.
32byte[32]asset_idasset ID of forwarded coins.
8*64byte[8][64]regsSaved registers from previous context.
8uint64codesizeCode size in bytes, padded to the next word boundary.
8byte[8]param1First parameter.
8byte[8]param2Second parameter.
1*byte[]codeZero-padded to 8-byte alignment, but individual instructions are not aligned.
Unwritable area ends.
*Call frame's stack.

After a call context has successfully ended, its call frame is popped from the stack. However, any space allocated on the heap during the execution of the call context persists in memory.

A call context returns its value using the $ret and $retl registers. Large return values can be written to the heap and later read from the caller contexts.

Memory Policies

After understanding the various contexts for executing the FuelVM, we now discuss the policies for reading and writing to memory in contexts.

Read Policies for Context

A context can read any data from the stack in the range from the byte at index 0 (i.e., from the start of the memory ) to the highest ever $sp and between the current $hp and VM_MAX_RAM (i.e., until the end of the memory ) of the previous context that created the current context.

Any attempt to read from the region between the highest ever $sp during the context execution and the current $hp will return an error.

2.1 Memory Read Policies

What do we mean by the highest ever $sp?

Since the stack can be grown and shrunk in size, it is possible that during the execution of some context, the $sp went until, for example, index 1000, but then elements were popped out of the stack, and now the current $sp is 900. In this scenario, the highest ever $sp during the execution of this call context is 1000, and hence, the memory region until 1000 is readable for the stack!

Write Policies for Context

A given context can write to any region between its $ssp and current $hp; hence, that memory region can be allocated and used for writing data.

Before writing to this memory region, allocate the bytes first. In the case of a stack, this is done using CFE and CFEI opcodes, while in the case of the heap, it is done via an ALOC opcode.

2.1 Memory Write Policies

Note: Remember that once a context completes, all values on the stack (i.e., the call frame and all values allocated on the stack during execution ) are wiped down. Still, heap allocation stays, and the following context can only write data below the $hp of the existing context.

VM Initialization & Configuration

Configuration

The VM can be configured by setting the following parameters:

nametypevaluenote
CONTRACT_MAX_SIZEuint64Maximum contract size, in bytes.
VM_MAX_RAMuint642**2664 MiB.
MESSAGE_MAX_DATA_SIZEuint64Maximum size of message data, in bytes.

VM Initialization

This section outlines the process during VM initialization for every VM run. To initialize the VM, the following pushes to the stack sequentially:

  1. Transaction hash (byte[32], word-aligned), computed as defined here.

  2. Base asset ID (byte[32], word-aligned)

  3. MAX_INPUTS pairs of (asset_id: byte[32], balance: uint64), of:

    1. For predicate estimation and predicate verification, zeroes.

    2. For script execution, the free balance for each asset ID seen in the transaction's inputs is ordered in ascending order. If there are fewer than MAX_INPUTS asset IDs, the pair has a zero value.

  4. Transaction length, in bytes (uint64, word-aligned).

  5. The transaction, serialized.

The following registers are then initialized (without explicit initialization, all registers are initialized to zero):

  1. $ssp = 32 + 32 + MAX_INPUTS*(32+8) + size(tx)): the writable stack area starts immediately after the serialized transaction in memory (see above).

  2. $sp = $ssp: writable stack area is empty to start.

  3. $hp = VM_MAX_RAM: the heap area begins at the top and is empty to start.

Further Readings

  • Nick Dodson’s tweet on what makes FuelVM unique: https://x.com/IAmNickDodson/status/1542516357886988288
  • Blockchain Capital’s blog on FuelVM and Sway: https://medium.com/blockchain-capital-blog/exploring-the-fuelvm-86cf9ccdc159
  • UTXO Model by River.com: https://river.com/learn/bitcoins-utxo-model/

Transactions on Fuel

Highlights:

  • Fuel utilizes the UTXO model for transactions, a method famously employed in the Bitcoin protocol. This method allows for advantages like parallel transaction execution. In this model, addresses can own native assets and spend these coins through transactions.

  • There are five distinct categories of transactions in Fuel, classified based on their operations: Script, Create, Mint, Upgrade, and Upload. Categorization helps define the various functionalities that users can perform within the Fuel ecosystem.

  • Fuel transactions are composed of several key components: Inputs, Scripts, Outputs, and Witnesses. Inputs consist of state elements that users access during the transaction and can include Coins, Contracts, and Messages.

  • The structure of a Fuel transaction allows for the inclusion of smart contracts as inputs, which can maintain persistent storage and can be utilized to execute complex operations beyond simple transactions, unlike the limitations faced by the Bitcoin protocol.

  • Witnesses play a crucial role in Fuel transactions by providing digital signatures and verification for spending coins. Block builders fill in these fields and exclude them from the transaction ID, allowing for flexible data handling in transaction processing.

Fuel uses the UTXO model for transactions on its blockchain. The model is popularly used in the Bitcoin protocol and has various advantages, including parallel transaction execution.

In Fuel, addresses can own native assets and spend coins with transactions. Fuel categorizes transactions into five types based on their blockchain operations:

  1. Script
  2. Create
  3. Mint
  4. Upgrade
  5. Upload

2.2 Transaction Types

Fuel uses the UTXO model for transactions, introducing specific constructs we'll explore before examining the various transaction types:

  • Inputs
  • Script
  • Outputs
  • Witnesses

We'll explore Fuel transaction components in detail before examining individual transaction types.

Inputs

Fuel transactions use three types of Inputs, which are state elements accessed by users:

  1. Coins
  2. Contracts
  3. Messages

Coins

Coins are units for some asset that a user can spend as part of the transaction. Fuel natively supports multiple assets, unlike chains that only support one base asset (such as ETH for Ethereum). Asset creation is built into Fuel's protocol. For more details, see the native assets section in the appendix.

Users can own various denominations of certain assets in different numbers of Coins. For example, a Fuel address A can have a balance of some asset 100, with four coins of 25 denominations each, and some address B can have a balance of 100 for the same asset, but three coins of denomination 10, 40, 50.

2.2 Input Coins

An Input Coin has the following parameters attached:

nametypedescription
txIDbyte[32]Hash of transaction.
outputIndexuint16Index of transaction output.
ownerbyte[32]Owning address or predicate root.
amountuint64Amount of coins.
asset_idbyte[32]Asset ID of the coins.
txPointerTXPointerPoints to the TX whose output is being spent.
witnessIndexuint16Index of witness that authorizes spending the coin.
predicateGasUseduint64Gas used by predicate.
predicateLengthuint64Length of predicate, in instructions.
predicateDataLengthuint64Length of predicate input data, in bytes.
predicatebyte[]Predicate bytecode.
predicateDatabyte[]Predicate input data (parameters).

The transaction invalidity rules for this input type can be seen here.

Contracts

A common question about the UTXO model concerns implementing smart contracts beyond ephemeral scripts.

Bitcoin's limited support for complex smart contracts stems from several core issues:

  • Bitcoin script is not Turing complete, meaning you cannot do things like loops inside Bitcoin

  • Bitcoin scripts in transactions lack persistent storage, limiting the blockchain's functionality.

Many incorrectly attribute Bitcoin's limitations to its UTXO model. However, these constraints stem from deliberate design choices. At Fuel, we embrace the UTXO model while supporting full Turing-complete smart contracts with persistent storage. We solve this problem by making stateful smart contracts an input for Fuel transactions.

Contracts have persistent storage and can own native assets. Users consume contracts by using the contracts as input for transactions. Then, users can call various external functions attached to contracts via the ephemeral script attached to the transaction.

2.2 Input Contracts

nametypedescription
txIDbyte[32]Hash of transaction.
outputIndexuint16Index of transaction output.
balanceRootbyte[32]Root of amount of coins owned by contract before transaction execution.
stateRootbyte[32]State root of contract before transaction execution.
txPointerTXPointerPoints to the TX whose output is being spent.
contractIDbyte[32]Contract ID.

When signing over contracts, txID, outputIndex, balanceRoot, stateRoot, and txPointer are initialized to zero values, which the block builder later fills in. This helps avoid concurrency issues with Contracts, as previously seen in the Cardano model.

When interacting with an AMM contract on Fuel, the process follows a specific flow. You begin by including the contract as an input to your transaction. Next, you call the external methods within an ephemeral script. Finally, you emit the contract as an output. This emitted contract can then be consumed as an input for the subsequent transaction involving this particular AMM contract. This approach allows for efficient state management and seamless interaction with the AMM on the Fuel platform.

The transaction invalidity rules for this input type can be seen here.

Messages

The Block Builder creates messages created as part of sending messages from the L1 to the L2. Messages make deposits to the Fuel rollups from the L1 possible, and we will discuss them in better detail later in the Fuel & Ethereum section.

NOTE: An Input Message can only be consumed as an Input as part of a transaction, and is then destroyed from the UTXO set.

2.2 Input Messages

A fuel InputMessage consists of the following parameters:

nametypedescription
senderbyte[32]The address of the message sender.
recipientbyte[32]The address or predicate root of the message recipient.
amountuint64Amount of base asset coins sent with message.
noncebyte[32]The message nonce.
witnessIndexuint16Index of witness that authorizes spending the coin.
predicateGasUseduint64Gas used by predicate execution.
dataLengthuint64Length of message data, in bytes.
predicateLengthuint64Length of predicate, in instructions.
predicateDataLengthuint64Length of predicate input data, in bytes.
databyte[]The message data.
predicatebyte[]Predicate bytecode.
predicateDatabyte[]Predicate input data (parameters).

The transaction invalidity rules for this input type can be seen here.

Scripts

Fuel scripts are ephemeral scripts that express the various actions taken during a transaction; a script can call the contracts provided as inputs or perform other arbitrary computation.

Fuel implements multi-call functionality through scripts, enabling efficient batch transactions. This approach allows users to:

  1. Provide up to MAX_INPUTS contracts in a single transaction

  2. Call external methods on these multiple contracts

As mentioned in the FuelVM section, the FuelVM is in the Script Context, scripts cannot have their own persistent storage.

Outputs

Fuel transactions have Outputs, which define the creation of new UTXOs post-transaction; these Outputs can then be inputs for the next set of transactions.

There are five types of possible Output types in a Fuel transaction:

  1. Coin
  2. Contract
  3. Change
  4. Variable
  5. ContractCreated

One thing to note is we have three Outputs dealing with Coins, and the table below summarizes the core differences (we will expand more in further sections):

OutputCoinOutputChangeOutputVariable
AmountStaticAutomatically setSet by script/contract
AssetIDStaticStaticSet by script/contract
ToStaticStaticSet by script/contract

NOTE: A Coin Output (Coin, Change, Variable) with an amount of zero leads to the pruning of the output from the UTXO set, which means coin outputs of amount zero are not part of the UTXO set.

OutputCoin

Output Coins are new coins sent to a Fuel Address, which become spendable as Input Coins in further transactions.

nametypedescription
tobyte[32]Receiving address or predicate root.
amountuint64Amount of coins to send.
asset_idbyte[32]Asset ID of coins.

2.2 Output Coins

The transaction invalidity rules for this output type can be seen here.

OutputContract

OutputContracts are newly generated contract outputs that become available as InputContracts for a specific contract ID in subsequent transactions utilizing this contract as an Input. They contain the newly updated index, balanceRoot, and stateRoot of the contract after being processed as part of the transaction.

NOTE: Every InputContract part of the transaction must always have a corresponding Output Contract.

2.2 Output Contract

nametypedescription
inputIndexuint16Index of input contract.
balanceRootbyte[32]Root of amount of coins owned by contract after transaction execution.
stateRootbyte[32]State root of contract after transaction execution.

The transaction invalidity rules for this output type can be seen here.

OutputChange

An OutputChange, included as one of our outputs for a specific assetId, enables the recovery of any unused balance from the total input balance provided in the transaction for that assetId.

For example, an OutputChange can collect any ETH not spent as gas or any USDC not swapped as part of a DEX transaction.

2.2 Output Change

NOTE: There can only be one OutputChange per asset_id in a transaction.

nametypedescription
tobyte[32]Receiving address or predicate root.
amountuint64Amount of coins to send.
asset_idbyte[32]Asset ID of coins.

The transaction invalidity rules for this output type can be seen here.

OutputVariable

OutputVariable acts as a placeholder for coins created in the execution of scripts and contracts since they can create a coin of an arbitrary amount and to an arbitrary user. This is useful in scenarios where the exact output amount and owner cannot be determined beforehand.

NOTE: This means every transaction using mint internally will need an OutputVariable for that particular assetID.

2.2 Output Variable

Consider a scenario where a contract transfers its output coin to a user only upon receiving a correct value. In this case, we can utilize a variable output at the end of the transaction. This output may or may not have a value attached to it, depending on whether the condition is met and have an arbitrary owner.

Variable Outputs are used via the TRO opcode.

nametypedescription
tobyte[32]Receiving address or predicate root.
amountuint64Amount of coins to send.
asset_idbyte[32]Asset ID of coins.

The transaction invalidity rules for this output type are available in our documentation.

OutputContractCreated

The OutputContractCreated output indicates that a new contract was created as part of the transaction. The parameters include the contractID and the initial state root for this contract.

nametypedescription
contractIDbyte[32]Contract ID.
stateRootbyte[32]Initial state root of contract.

The transaction invalidity rules for this output type can be seen here.

Witness

The witness is a parameter attached to transactions. The block builders fill in witnesses and are not part of the transaction ID. A Witness is usually used to provide digital signatures for verification purposes, for example, the signature to prove the spending of a Coin or anything else.

Witnesses are not part of the transaction ID, which allows someone to sign over a transaction and provide it as part of the transaction.

NOTE: The protocol doesn't limit witnesses to providing signatures only; they serve to fill in any data and enable various interesting use cases, like State Rehydration.

Each witness contains a byte array data along with the field dataLength helping know the length of this data.

nametypedescription
dataLengthuint64Length of witness data, in bytes.
databyte[]Witness data.
asset_idbyte[32]Asset ID of coins.

Multiple witnesses can be provided as part of the transaction, and the inputs can indicate which witness block builders, contracts, scripts or predicates can look at to verify the validity of being able to spend the input by providing the index at which their witness lives.

TransactionScript

Script transactions are transactions that, as the name suggests, have Inputs, Outputs, and a Script that dictates what happens as part of the transaction.

Note: Scripts are optional in transactions of type TransactionScript. For example, a simple token transfer can work only on inputs and outputs, with no requirement for a script. Scripts are mainly leveraged when you want to do other things as part of your transaction beyond simply transferring or burning assets.

The transaction's script can compute arbitrary amounts and call other contracts. A famous example of script transactions is using an AMM or transferring a token.

nametypedescription
scriptGasLimituint64Gas limits the script execution.
receiptsRootbyte[32]Merkle root of receipts.
scriptLengthuint64Script length, in instructions.
scriptDataLengthuint64Length of script input data, in bytes.
policyTypesuint32Bitfield of used policy types.
inputsCountuint16Number of inputs.
outputsCountuint16Number of outputs.
witnessesCountuint16Number of witnesses.
scriptbyte[]Script to execute.
scriptDatabyte[]Script input data (parameters).
policiesPolicy []List of policies, sorted by PolicyType.
inputsInput []List of inputs.
outputsOutput []List of outputs.
witnessesWitness []List of witnesses.

NOTE: Script transactions lack the ability to create contracts, therefore they cannot produce a ContractCreated output type. For additional transaction invalidity rules, refer to our documentation.

TransactionCreate

TransactionCreate is used to create new contracts; the parameters allow for contracts with initialized storage slots.

The contract ID of smart contracts on Fuel is calculated deterministically, and the calculation mechanism is referred to here.

2.2 Transaction Create

nametypedescription
bytecodeWitnessIndexuint16Witness index of contract bytecode to create.
saltbyte[32]Salt.
storageSlotsCountuint64Number of storage slots to initialize.
policyTypesuint32Bitfield of used policy types.
inputsCountuint16Number of inputs.
outputsCountuint16Number of outputs.
witnessesCountuint16Number of witnesses.
storageSlots(byte[32], byte[32])[]List of storage slots to initialize (key, value).
policiesPolicy []List of policies.
inputsInput []List of inputs.
outputsOutput []List of outputs.
witnessesWitness []List of witnesses.

The transaction invalidity rules for this transaction type can be seen here.

TransactionMint

The block producer uses this transaction to mint new assets. It doesn’t require a signature. The transaction is currently used to create the block producer's fees. The last transaction in the blocks is a Coinbase transaction, allowing the block producer to collect fees for building the block.

2.2 Transaction Mint

nametypedescription
txPointerTXPointerThe location of the Mint transaction in the block.
inputContractInputContractThe contract UTXO that assets are minted to.
outputContractOutputContractThe contract UTXO that assets are being minted to.
mintAmountuint64The amount of funds minted.
mintAssetIdbyte[32]The asset IDs corresponding to the minted amount.
gasPriceuint64The gas price to be used in calculating all fees for transactions on block

The transaction invalidity rules for this transaction type can be seen here.

TransactionUpgrade

The Fuel network employs consensus parameters, subject to occasional upgrades. The network's state transition function resides on-chain, allowing privileged addresses to upgrade it when necessary.

Therefore, at any given moment, a TransactionUpgrade might attempt to perform one of the following actions:

  • Trying to upgrade the consensus parameters

  • Trying to upgrade the state transition function

2.2 Transaction Upgrade

nametypedescription
upgradePurposeUpgradePurposeThe purpose of the upgrade.
policyTypesuint32Bitfield of used policy types.
inputsCountuint16Number of inputs.
outputsCountuint16Number of outputs.
witnessesCountuint16Number of witnesses.
policiesPolicy []List of policies.
inputsInput []List of inputs.
outputsOutput []List of outputs.
witnessesWitness []List of witnesses.

The transaction invalidity rules for this transaction type can be seen here.

TransactionUpload

Before performing an upgrade, operators must upload the Fuel state transition bytecode to the chain. This requires uploading the bytecode via multiple transactions. TransactionUpload allows us to split the bytecode into multiple subsections and upload each subsection sequentially over multiple transactions.

On successful upload of all subsections, the transaction reaches completion, and the system adopts the new bytecode.

2.2 Transaction Upload

nametypedescription
rootbyte[32]The root of the Merkle tree is created over the bytecode.
witnessIndexuint16The witness index of the subsection of the bytecode.
subsectionIndexuint16The index of the subsection of the bytecode.
subsectionsNumberuint16The total number of subsections on which bytecode was divided.
proofSetCountuint16Number of Merkle nodes in the proof.
policyTypesuint32Bitfield of used policy types.
inputsCountuint16Number of inputs.
outputsCountuint16Number of outputs.
witnessesCountuint16Number of witnesses.
proofSetbyte[32][]The proof set of Merkle nodes to verify the connection of the subsection to the root.
policiesPolicy []List of policies.
inputsInput []List of inputs.
outputsOutput []List of outputs.
witnessesWitness []List of witnesses.

The transaction invalidity rules for this transaction type can be seen here.

Appendix

Native Assets

In Fuel, apart from Eth (which is called the base asset), the functionality of minting and burning assets is enshrined in the protocol. The FuelVM provides op-codes for creating, minting, and burning assets, MINT and BURN, respectively.

All native assets can be spent using the same rules as the base asset, allowing Fuel developers and users to fully utilize the UTXO model and the resulting parallelization.

To explore Native assets further, it is recommended that you look at Sway Standards, which provide various standards ( like SRC-3, SRC-20, and many more ) related to native assets.

Fuel Blocks

Highlights:

  • In Fuel, blocks are constructed by Block Builders, who process both transactions and messages to create the blocks. Users send transactions either directly to the builder or through layer 1, while messages originate from layer 1. The Fuel and Ethereum section of the book provides further details on this process.

  • Each Fuel block begins with a header that consists of three main fields: the Application Header, the Consensus Header, and Block Header Metadata. This structure facilitates efficient management and processing of block-related information.

  • The Application Header records critical operational details for the Fuel rollup, comprising four essential components: the da_height, consensus_parameters_version, state_transition_bytecode_version, and generated fields. These components work together to ensure the rollup operates correctly and efficiently.

  • The Consensus Header tracks the hash of the Application Header, providing a secure and verifiable method for maintaining consensus within the Fuel network. This header is crucial for ensuring the integrity of the block.

  • Fuel blocks also include a Coinbase transaction, which enables block producers to collect fees for their work. This Mint transaction must be the last in the block and is capped at the total fees processed from all transactions within that block, ensuring a fair and controlled fee structure.

Blocks in Fuel are built by entities called Block Builders. Fuel blocks are made by processing transactions and messages. Transactions can be sent directly to the builder or via layer 1, while messages are sent from layer 1. In the Fuel and Ethereum section of the book, we expand further on messages and transactions sent from the L1.

Block Header

A Fuel block header at the top consists of three fields:

  • Application Header
  • Consensus Header
  • Block Header Metadata
pub struct BlockHeaderV1 {

    pub application: ApplicationHeader<GeneratedApplicationFields>,

    pub consensus: ConsensusHeader<GeneratedConsensusFields>,

    metadata: Option<BlockHeaderMetadata>,
}

Application Header

The application header records essential information regarding the operation of the Fuel rollup.

The application header at the high level consists of four essential components:

pub struct ApplicationHeader<Generated> {

    pub da_height: DaBlockHeight,
 
    pub consensus_parameters_version: ConsensusParametersVersion,

    pub state_transition_bytecode_version: StateTransitionBytecodeVersion,

    pub generated: Generated,
}

da_height

The da_height field records the latest block L1 block until the messages sent from the L1 → L2 have been processed; this is helpful later in fraud proving to establish that a particular message was sent from the L1 to the L2 rollup but wasn’t processed as part of the block that included the messages up to the block of which it was part.

consensus_parameters_version

The Fuel rollup has a set of upgradeable consensus parameters, which are upgradable via Transactions of type Upgrade. For each consensus parameter upgrade, a new version for consensus_paramters_version must be assigned, helping us track which set of consensus parameters we are using while building a particular block.

state_transition_bytecode_version

The Fuel rollups keep the WASM compiled bytecode of their state transition function as part of the chain facilitating forkless upgrades for the Fuel rollups.

The new state transition function is uploaded via the Upload transactions, while the upgrade is done via the Upgrade transactions. Each upgrade updates the state_transition_bytecode_version, and this version helps keep track of which state transition function is being used to process transactions for a given block.

generated

The section contains various rollup-specific fields around execution for a specific block. The Fuel flagship rollup has the following fields for generated:

pub struct GeneratedApplicationFields {
    /// Number of transactions in this block.
    pub transactions_count: u16,
    /// Number of message receipts in this block.
    pub message_receipt_count: u32,
    /// Merkle root of transactions.
    pub transactions_root: Bytes32,
    /// Merkle root of message receipts in this block.
    pub message_outbox_root: Bytes32,
    /// Root hash of all imported events from L1
    pub event_inbox_root: Bytes32,
}

Consensus Header

The consensus header is another top-level field for the Block Header for Fuel rollups, it is configurable and for the flagship Fuel rollup only keeps track of the hash of the Application Header.

pub struct GeneratedConsensusFields {
    /// Hash of the application header.
    pub application_hash: Bytes32,
}

Block Header Metadata

The Block Header Metadata is used to track metadata. The current flagship Fuel rollup includes a field tracking the block ID, which represents the hash of the block header.

pub struct BlockHeaderMetadata {
    /// Hash of the header.
    id: BlockId,
}

Coinbase Transaction

Fuel blocks contain a Coinbase transaction; block producers use Coinbase transactions to collect fees for building blocks. The Coinbase transaction is a Mint transaction, where the mintAmount cannot exceed the fees processed from all transactions in the block. The protocol also requires the Coinbase transaction to always be the last transaction in the block.

Block Building in Fuel

Highlights:

  • The Block Builder plays a pivotal role in block building within Fuel by processing messages and transactions, constructing blocks, and submitting them to Layer 1 while ensuring soft-finality on Layer 2.

  • The Fuel Block Builder uses a messaging system to facilitate the transfer of information between Layer 1 and Layer 2, enabling both regular transactions and forced transaction inclusion. This feature empowers users to bypass potential censorship by relaying their transactions directly from Layer 1.

  • To guarantee reliable processing of messages and transactions, the Block Builder appends a data height (da_height) to each block, linking it to a specific Layer 1 block. By utilizing Merkle trees to store commitments, the system ensures that all events are processed in a deterministic manner, allowing for transparent validation and slashing of the Block Builder if necessary.

  • The Block Builder also processes local transactions, allowing users to send transactions directly to it for more efficient batching and compression. This method reduces transaction costs and accelerates soft finality, providing users - with quicker confirmations compared to traditional Layer 1 submissions.

  • The Block Builder determines transaction ordering and manages miner extractable value (MEV) in Fuel. It prioritizes transactions based on the tips provided, contrasting with the first-in-first-out approach found in many other Layer 2 solutions, which optimizes user incentives within the network.

This section focuses on block building in Fuel and the role that the Block Builder plays in this process.

The Fuel Block Builder is a component in Fuel rollups, which is responsible for:

  • Processing messages from L1 → L2

  • Processing transactions in the mempool

  • Building blocks and submitting them to the Layer 1

  • Providing soft-finality on the Layer 2

L1 → L2 processing

Fuel rollups have a messaging system (from L1 → L2 and vice versa), which we will discuss further in the next section on bridging. In addition to relaying bridge messages, this system allows transactions to be sent directly from L1, which is used for forced transaction inclusion.

The Fuel Block Builder uses a relay to receive messages and transactions from L1 → L2, we will discuss both of these cases individually now.

L1 Messages

Block Builder receives relayed messages from Layer 1 emitted as L1 events. The message is then picked up as part of the block-building process; each message sent from the L1 has the following format:

nametypedescription
senderbytes[32]The identity of the sender of the message on the L1
recipientbytes[32]The recipient of the message on the Fuel Blockchain
noncebytes[32]Unique identifier of the message assigned by the L1 contract
amountuint64The amount of the base asset transfer
databyte[]Arbitrary message data

The block builder creates an output of type OutputMessage, and after creating this output, it completes the message processing.

Applications can leverage these OutputMessage(s) as they see fit. One example is the deposit process, where the bridge contract mints new ETH on the L2 after receiving specific messages that prove deposits on the L1 (we will discuss this further in the next section).

L1 Transactions and Forced Inclusion

Fuel provides forced inclusion of transactions. If a user feels the L2 block builder is attempting to censor, they can emit a serialized transaction from the L1 as an event, forcing the L2 block builder to include the transaction in the block building. This process, called “Forced Inclusion,” guarantees user censorship resistance.

The Fuel transactions sent from L1 are emitted as events via the L1 and have the following format:

nametypedescription
noncebytes[32]Unique identifier of the transaction assigned by the L1 contract
max_gasuint64The maximum amount of gas allowed to use on Fuel Blockchain
serialized_transactionbyte[]The serialized transaction bytes following canonical serialization

Forced Inclusion allows the processing of all transaction types except Mint, which can only be created by the Fuel Block Builder. This exception does not restrict security guarantees for users' censorship resistance.

Guarantees around L1 processing

How does the L2 guarantee it will always process messages or transactions sent from L1?

This is done by appending the da_height, i.e., the L1 block up to which the current block processes messages and transactions. A commitment for all the events and transactions is stored as part of the block header, using a Merkle tree and its root.

All events from L1 → L2 (both inbox messages and forced transactions), are ordered by their block number and the index in that block. Following this order allows us to find a deterministic way of creating this Merkle tree.

We create this Merkle tree and store the root in the event_inbox_root field as part of the block header.

Fuel blocks are subject to later challenges. If it's proven that a specific message or transaction was omitted or not processed, the responsible block builder can be penalized.

Processing Local Transactions

Apart from processing messages and transactions from L1 → L2, the Block Builder is responsible for processing transactions sent to it locally. Users can send transactions to the Block Builder locally, collected in its Mempool, and then processed and sent to Layer 1.

By using clever batching and compression techniques (gzip or zstd) this system offers users lower transaction costs compared to direct Layer 1 submissions.

Another advantage of sending transactions directly to the Block Builder is getting faster soft finality on the L2. For a transaction sent via L1 to be processed, the L1 block must be finalized first.

Block Building and Proposing

The Fuel Block Builder is required to bundle transactions into blocks and propose them to Layer 1 as part of processing transactions. Committed blocks on Fuel enter a 'Challenge Window' after commitment. Once this window closes, the block and its corresponding state are considered to have reached 'L1 finality'.

Fuel Block Builder currently sends the block hash and block height as new updates to the onchain message portal, along with blobs containing transactions and other data, to provide DA for that specific block.

Transaction Ordering and MEV

The current Fuel Block Builder decides the priority of a transaction by sorting with tip/max_gas, which means unlike many other L2s, the network isn’t FIFO (First In First Out); this also means that in Fuel, the Priority of your transaction inclusion is:

  • Directly proportional to the tip you provide as part of the transaction

  • Inversely proportional to the max_gas you permit for your transaction

Soft Finality

The Block Builder also plays a major role in providing soft finality for L2 transactions. As an L2 participant, you can choose the level of finality at which you're comfortable making business decisions.

When the Block Builder orders and processes your transaction, it provides a soft finality. This can be considered confirmed unless the Block Builder fails to finalize it on L1.

Appendix

Full Nodes

The fuel-core software also allows you to run a Full Node. A Full Node collects the latest updates on Layer 2 from the peers and broadcasts incoming transactions to the network.

Full Nodes cannot build blocks; instead, they receive them as updates via p2p and re-execute them locally to maintain the correct state with complete verification.

By running the Full Node, you can, as a user, be given the ability to keep verifying the L2 yourself and, hence, also be able to send fraud proofs. You also get your own GraphQL endpoint, which can broadcast your transactions to the network.

All Fuel GraphQL providers run Full Nodes themselves to provide you with the latest Fuel state and allow you to broadcast transactions.

Fuel and Ethereum

Highlights:

  • Fuel Ignition leverages Ethereum as its Layer 1 for settlement and data availability, aligning with Ethereum's core values of sustainability, security, and accessibility for everyday users. This choice emphasizes Fuel's commitment to building a long-term, decentralized ecosystem.

  • By utilizing one of the most established and decentralized Layer 1 networks, Ethereum provides a robust foundation for Fuel’s rollup, ensuring reliable performance and security. Fuel's rollup inherits Ethereum's security model, which safeguards user funds and enables fraud-proof mechanisms directly on the Ethereum blockchain.

  • Fuel allows seamless messaging between Layer 1 and Layer 2, ensuring that any message sent to Ethereum must be processed on Fuel and vice versa. This capability enhances user experience and guarantees censorship resistance.

  • Users can easily deposit and withdraw ETH, transferring assets between Layer 1 and Layer 2. They can deposit ETH directly to Fuel and initiate withdrawals by burning tokens on Layer 2, with the system ensuring timely and secure processing.

  • Fuel actively pursues innovation through techniques like hybrid proving, optimizing the proving system by reducing complexity and shortening challenge windows. By embracing a modular tech stack, Fuel remains adaptable, exploring integration with alternative Layer 1s and data availability solutions to enhance its ecosystem.

Fuel Ignition uses Ethereum as a Layer 1. We chose Ethereum as Fuel’s L1 for both Settlement and Data availability of the L2, because we think Fuel shares many of Ethereum’s values:

  • Building for long-term sustainably

  • Building with an emphasis on security

  • Focus on consumer hardware and making participation in the protocol accessible for ordinary people

Ethereum is one of the most decentralized L2s. Ethereum has a long-standing presence and has focused on a rollup-centric roadmap for years. These factors make it the ideal foundation for building a rollup.

2.5 Fuel and Ethereum

Inheriting Ethereum’s Security

Fuel’s flagship rollup, Ignition, inherits Ethereum’s security. The natural question from the previous statement is, what do we mean by inheriting Ethereum’s security?

Fuel uses Ethereum as the layer to keep users’ funds and propose its latest blocks and corresponding state updates. We deploy smart contracts that continuously get updates of Fuel Layer 2.

Then, we have fraud-proving performed directly on the Ethereum L1 to prove that something about the posted blocks or related state updates is wrong. We also allow permissionless messaging and transaction inclusion via the L1 to ensure the user doesn’t experience any censorship resistance.

This gives the user guarantees that as long as Ethereum is secure and the honest majority assumption for it is held:

  • No fraud blocks or state updates can be sent
  • Their funds are always safe on the Layer 1
  • They can never be stopped from withdrawing them or being able to send any transaction to the L2 (forced inclusion)

Now, we will discuss each of the properties we described above.

Messaging

Fuel allows for messaging between L1 → L2 and L2 → L1, which means you can send any arbitrary message from Layer 1 to Layer 2 and vice versa. The protocol guarantees that if a message is included in the L1, it has to be processed on the L2 and vice versa. Let’s discuss both of these cases individually.

L1 → L2 Messaging

The Fuel Message Portal facilitates message processing from L1 -> L2. Its method, sendMessage, accepts the L2 recipient (a Fuel Address) and the corresponding message to be sent. After a successful call to this method, a MessageSent event is emitted on Layer 1.

As discussed in the section on block building, part of processing the Fuel blocks requires committing to some L1 block height, up to which the block builder processes messages and transactions, this forces the Block builder to include all messages from the L1 (as in case of failure, the builder can be slashed).

2.5 L1 → L2 Messaging

As part of processing the message blocks from the L1, the block builder looks at the event and mints an OutputMessage transaction to the particular Fuel address with the specific data.

L2 → L1 Messaging

Fuel also allows messages from the L2 -> L1 to be sent using MessageOut receipts. Every Fuel block includes a receipt root, the root of all receipts that were part of the block. This allows anyone to make a call to the relayMessage function of the Fuel Message Portal; a Merkle proof of inclusion is required to perform for the message you are trying to process along with that, it checks whether the block for which the message is being processed has been finalized or not (i.e., it outside of the challenge window).

2.5 L2 → L1 Messaging

Processing the message on the L1 coming from the L2 is done by calling the specific L1 address to which the message is sent to with some desired payload.

ETH Deposits and Withdrawals

A core part of using the Fuel rollup is depositing ETH from the L1 to Fuel and withdrawing it from the L2. We will discuss both of these scenarios individually.

ETH Deposits

The user can call the depositEth function on the L1 to create a deposit. The method is payable, and emits a messageSent event with an empty payload, this makes the sequencer recognize this is a deposit made on the L1 and it mints a new eth coin corresponding to the value of the deposit for the user.

ETH Withdrawals

Withdrawals on the L2 are made by burning the tokens on the L2 via the L2 gateway. Then, the gateway emits a MessageOut receipt, which is part of the block header, allowing the relay of this message to Layer 1.

The Layer 1 Message Portal contract has a relayMessage function (read L2 → L1 messaging for details); which allows for processing L2 messages aimed for L1, in the case of withdrawals, we send a message with the amount corresponding to the value the user has burned on the L2, and hence the Message Portal contract provides the L1 recipient with their funds for withdrawal.

Note: A withdrawal requires the 'Challenge Window' to be cleared before being processed, and hence the user has to wait till the 'Challenge Window' (although there are fast finality gadgets which can bring this down.)

State Updates

Fuel uses Ethereum to submit new state updates. This is done using the State Contract on Layer 1, where the blocks are committed by sending the block hash and block height. The contract also records the timestamp as part of the commitment for a particular block.

These state updates and the data posted as Blobs on Ethereum allow for challenging any state updates sent to the L1.

Challenge Window

The challenge window is the time it takes for a block and related state posted on the L1 to be considered finalized. Finalization means any withdrawal or message part of this block can be processed on the L1. For now, the challenge window for Fuel is seven days.

Techniques like hybrid proving and other fast finality gadgets can reduce the duration of the challenge window; we are actively researching these areas and would encourage you to read Nick Dodson’s post on faster finality gadgets for optimistic rollups.

Hybrid Proving

Fuel believes in a philosophy of zk-pragmatism; rather than playing bisection games on-chain like other rollups (which increase the complexity of the proving system) or sending zk proofs for every bath like zk rollups (which increase the cost per transaction), Fuel makes a hybrid approach for its proving system.

The system runs in an optimistic setting. If someone in the system believes that a fraud state has been sent, they create a zk-proof off-chain of the claim and prove fraud in a single interaction with the L1. This reduces the proving system's complexity and limits the challenge window.

Hybrid proving is being developed, and prototyping is done with RISC-V-based zkVMs like SP-1 and RISC-0. You can read more about the proving system here.

Appendix

Alt-DAs and L1s

We have launched our flagship rollup with Eth as our L1 for settlement and data availability, but Fuel believes in creating a neutral and modular tech stack. The Fuel tech stack can be extended to launch on alt L1s like Bitcoin and Solana and with alt DAs like Celestia and Avail. If someone wants, they can even use the Fuel stack to launch their L1.

We will keep progressing our tech stack to be adaptable in multiple scenarios, resilient, and feasible on consumer-grade hardware.

Blobs

EIP 4844 introduced Blobs as a cheaper way to get Data Availability for Ethereum rollups. Fuel block builders also use blobs, although this is a work in progress.

Fuel blocks are batched together in a bundle, compressed via popular techniques (gzip or zstd), and posted as blobs. Because blobs are fixed in size, uploading has to be done via a series of transactions.

Blobs and their exact implementation are still being finalized and will be live soon, but the above text summarizes the general approach for now.

Security on Fuel

Highlights:

  • Fuel emphasizes security across its rollup ecosystem, particularly through its Security Council, which operates multi-signature wallets to oversee and upgrade various components of the stack. This council plays a critical role in safeguarding the network as Fuel navigates its transition to a fully permissionless system.

  • The project recognizes the current centralization of block building and sequencing as a challenge. To address this, Fuel plans a phased approach to decentralize these processes, beginning with a shared sequencer accessible to all block builders before moving towards fully decentralized block building.

  • Fuel proactively identifies and mitigates potential security attack vectors, including bugs in bridge contracts, Layer 2 client implementations, and the Sway compiler. By collaborating with diverse teams and engaging top security organizations, Fuel works to establish robust security protocols and multiple implementations to reduce the likelihood of vulnerabilities.

  • The platform also tackles application-level bugs by developing secure support libraries in Sway and promoting best practices among developers. This focus on security fosters a more resilient ecosystem for application development.

  • Fuel continuously upgrades its protocol to further enhance security, reducing reliance on the Security Council and minimizing risks associated with multi-signature compromise. The project also prioritizes rigorous auditing and testing of fraud-proving implementations to protect against false claims and ensure that legitimate builders receive fair treatment.

This section discusses the current security of Fuel Ignition and rollups.

Fuel Security Council

Fuel currently has a security council that operates various multi-sigs to upgrade different parts of the stack.

Rollups are in an early stage of development, necessitating a security council to exercise caution before full decentralization. Network issues can be difficult to resolve, making this oversight crucial.

Developing a stack with type-2 security guarantees is a top priority in Fuel's roadmap.

Block Building

Currently, block building and sequencing are centralized. Decentralizing these processes, especially block building, requires further development and careful consideration. Block building rights give the builder access to extract MEV from the system, which can impact user’s transactions and experience on the network.

Fuel is implementing a phased approach to decentralize block builders and sequencers.

Fuel will start with a decentralized sequencer set, i.e., a shared sequencer that block builders can use for all Fuel rollups. Decentralized block building will follow in the next phase.

Security Attack Vectors

In this section, we list various attack vectors for the current system and we explore a path forward to tightening security.

Bridge Contract Bugs

Fuel has a bridge that allows for messaging between L1 and L2; this messaging system creates the base for building a deposit and withdrawal system along with abilities like forced transaction inclusion and calling contracts L2 on L1.

If a bug in the contract implementation on the L1 or L2 compromises the roll-up system, which can include relaying fake messages and transactions from the L1 and L2. A compromise of the mult-sig can also lead to potential malicious upgrades of these contracts.

Fuel undergoes rigorous audits of its smart contracts with the best-in-class security auditors in the space and also participates in bug bounties to keep the possibility of this very low. These issues become more concerning in stage 2 settings, as the stage 1 setting does allow for reverting potential issues regarding bridge contracts.

Layer 2 Client Bugs

Like any software, the Fuel execution client could have bugs that might be exploited to enable unintended behavior. If there is only one implementation of the execution client, a malicious actor might manipulate the system without being caught by a fraud-proof mechanism, as no alternative client would exist to validate or challenge the state. This risk is particularly relevant in L2 solutions that rely on a single execution client for ZK proving games or fraud-proof verification, making it a potential attack vector.

Fuel attempts to strengthen security around such scenarios by inviting various teams to collaborate on the stack and aiming for multiple implementations, followed by rigorous testing and security audits by the best security organizations in the industry.

Sway Compiler Bugs

The Sway Language is a dominant language built on the Fuel VM. A bug in the Sway compiler could allow malicious bytecode to be part of a particular predicate, smart contract, or script, which the implementation didn’t desire. A similar issue was seen in the ETH ecosystem with Vyper, which you can follow here.

Fuel aims to avoid such a scenario by having some of the best talent working on its compiler, followed by rigorous testing and audits by leading security organizations in the industry. In the future, we also aim to have multiple implementations of the compiler, which could help discover a bug in the other implementations.

Application Level Bugs

Application implementations often have bugs because they avoid some required checks or are built on top of libraries with an underlying bug.

Fuel aims to avoid these by creating best-in-class support libraries in Sway, which are well-audited and tested and safe to build on. These Sway libraries also promotes the usage of secure patterns through developer evangelism.

Multisig Compromisation

If compromised, the security council's multi-sig can lead to severe issues, such as malicious upgrades or behavior in various parts of the stack.

Fuel aims to solve this by having a security council with a very high reputation and a lot of social capital attached to it. At the same time, continuous protocol upgrades minimize the need for the security council and always accelerate towards allowing for a stage 2 rollup stack.

Fraud Proving Bugs

A bug in the fraud-proving implementation can cause challenges and slash for block builders who built correct blocks or could allow someone to fail to prove a faulty block. This can result in good builders being slashed or wrong states being finalized.

Fuel aims to solve this by initially correcting any such issues with the help of the security council while aiming for multiple implementations of the fraud-proving client or, if possible, multiple-proving system. The implementation is done with best security practices in mind and with regular system audits.

Fees on Fuel

The Fuel ignition process introduces various fees and costs essential for utilizing the permissionless network. These can be categorized into the following types:

  • Transaction Fees: A mandatory fee paid to validators for processing transactions and executing instructions on the network.
  • Tip: An optional fee that allows users to boost their transactions in the processing order, ensuring faster execution.

Fuel’s Approach to Transaction Fees

Fee Structure

Fuel operates on a modular execution layer, emphasizing efficiency in gas computation and storage fees. Instead of relying on inflationary rewards, Fuel focuses on sustainable economics driven by transaction fees. Learn more.

Parallel Execution for Low Fees

Fuel’s unique parallel transaction execution significantly reduces congestion. This results in:

  • Lower transaction fees.
  • Faster settlement times, which encourages a high volume of transactions.

This aligns with the long-term vision of most blockchains to sustain security through transaction fees rather than inflation.

Transaction Fees in the Fuel Network

In the Fuel network, transaction fees are essential for incentivizing block builders to prioritize transactions and for maintaining network security and efficiency. Understanding how transaction fees are calculated and how they are used helps users make informed decisions on how to interact with the network.

What Are Transaction Fees?

Transaction fees in Fuel are the costs that users must pay to process and execute their transactions on the network. These fees are dynamic and depend on several factors, including the gas consumed by the transaction, the gas price, and the gas limit set by the user.

Transaction fees are calculated based on gas consumption during the execution of the transaction. There are two main components that determine the cost:

  • Intrinsic Fees: The fundamental costs associated with a transaction, including the byte size, processing of inputs/outputs, predicates and signature verification, and initializing the virtual machine (VM). These fees are incurred regardless of whether the transaction is successfully executed.
  • Execution Fees: Costs associated with the computational work performed during the transaction, determined by the complexity of operations such as smart contract execution, data manipulation, and VM computations.

Gas Consumption Breakdown

  • Intrinsic Gas Fees: These cover the basic costs of transaction handling, which include:

    • Byte size of the transaction.
    • Input/output processing.
    • Predicate evaluation and signature verification.
    • VM initialization (prior to executing scripts or predicates).
  • Execution Gas Fees: These fees account for the gas consumed during the execution phase of the transaction, such as:

    • Smart contract execution.
    • Data processing and storage.
    • Interactions with decentralized applications (dApps) or other smart contracts.
      The total fee for a transaction is calculated using the following formula:
def cost(tx, gasPrice) -> int:
   return gas_to_fee(min_gas(tx) + tx.gasLimit - unspentGas, gasPrice)

Where:

  • min_gas(tx): Minimum gas required for the transaction, covering intrinsic fees.
  • tx.gasLimit: The maximum amount of gas that the user is willing to spend for this transaction.
  • unspentGas: Gas left over after intrinsic costs and execution. This is collected by the block producer as a reward in the Fuel network.
  • gas_to_fee(): Converts the total gas used (after considering min gas, gas limit, and unspent gas) into a fee based on the gasPrice.

The final transaction fee depends on the amount of gas consumed during execution and the gas price specified by the user.

If the transaction uses less gas than the gas limit set by the user, the leftover gas (referred to as unspent gas) is collected by block builder as a reward.

The block gas limit is 30000000

Tips in the Fuel Network

Tips are an additional fee provided by the user to incentivize block builders to prioritize their transaction. In Fuel, the priority of your transaction’s inclusion in the block is determined by both the tip and the max_gas (gas limit) you set for the transaction.

What is a Tip?

A tip is an extra fee that the user adds on top of the minimum transaction fees to increase the likelihood that their transaction will be included in the next block. This tip directly incentivizes the block builder to prioritize the transaction.

  • Setting the Tip in a Transaction

To set the tip in a transaction using the Fuel SDK, you can specify it in the transaction parameters. Here’s an example in TypeScript using the fuels library:

import { bn, ScriptTransactionRequest } from 'fuels';

const transactionRequest = new ScriptTransactionRequest({
  tip: bn(10), // Sets the tip policy
  maxFee: bn(1), // Sets the max fee policy
});

In this example, the tip is set to 10 using the bn function to handle big numbers. The tip is an optional amount added to incentivize the block producer to include the transaction, ensuring faster processing for those willing to pay more.

Fee Structure and Incentives

Fuel uses a dynamic fee model where transaction fees are adjusted based on network congestion, transaction complexity, and the user’s willingness to pay higher fees. Block builders are incentivized based on both the tip and max_gas (gas limit), creating a flexible and efficient system.

Transaction Prioritization

In Fuel, transactions are prioritized by the block builder based on two main factors:

  • Tip: The additional gas fee provided by the user to incentivize faster processing of the transaction. A higher tip means higher priority for inclusion in the block.
  • max_gas: The maximum gas limit specified by the user for the transaction. A higher max_gas means the transaction may take longer to process, lowering its priority.

The priority of a transaction is:

  • Directly proportional to the tip: Higher tips increase the transaction’s priority.
  • Inversely proportional to the max_gas: A higher gas limit decreases the transaction’s priority.

This model encourages users to offer a higher tip to ensure quicker inclusion, while also considering the transaction’s gas limit to avoid excessively large transactions.

Unspent Gas and Block Producer Rewards

After a transaction is executed, any leftover gas (unspentGas) is collected by the block producer as a reward.

  • UnspentGas: The remaining Gas left over after intrinsic costs and execution. This is collected by the block producer as a reward in the Fuel
  • Block Producer Incentives: Block producers are rewarded for processing transactions, both through the minimum fee (guaranteed for each transaction) and the unspent gas collected.

The unspent gas ensures that block producers are incentivized to prioritize transactions with higher tips and to optimize block space for better overall performance.

Example Gas Calculation

Here’s a simplified example of how gas works in Fuel:

  • Transaction: A user sends tokens to another account.
  • Gas Calculation:
    • Compute Gas: CPU work required to validate the transaction.
    • Storage Gas: Updating the account balance in the blockchain.

If the gas limit is set to 10,000 and the gas price is 1 gwei, the total fee would be:

  • 10,000 x 1 gwei = 10,000 gwei (or 0.00001 ETH ).

When a transaction involves script execution, the system sets up the virtual machine (VM) to run the transaction. It checks the amount of gas available for the transaction and starts running the script step by step.

For each step in the script, the system calculates how much gas it needs. If there isn’t enough gas left to run a step, it stops the process and “reverts” the transaction, meaning nothing changes except for the gas spent. If there’s enough gas, it continues running the script and deducts the gas used. Learn more

At the end of the transaction, the system updates a record (called the receiptsRoot) to show the results of the transaction. This process helps ensure that the transaction is handled efficiently and fairly, with gas being used properly.

Fee Calculation Example

Let’s consider a transaction with the following parameters:

  • Intrinsic Gas (min_gas(tx)): 10,000 gas
  • Gas Limit (tx.gasLimit): 50,000 gas
  • Unspent Gas (unspentGas): 5,000 gas
  • Gas Price (gasPrice): 0.001 Fuel per gas unit

The transaction fee will be calculated as:

min_gas = 10000
gasLimit = 50000
unspentGas = 5000
gasPrice = 0.001

# Total gas used for the transaction
totalGas = min_gas + gasLimit - unspentGas

# Convert gas to fee
fee = gas_to_fee(totalGas, gasPrice)

This fee is the final cost that the user will pay for the transaction, which includes both intrinsic and execution gas fees, adjusted based on the gas price. Note that gasLimit applies to transactions of type Script. gasLimit is not applicable for transactions of type Create and is defined to equal 0 in the above formula.

Transaction Parameters

The following are the available parameters to control transaction behavior:

const txParams: TxParams = {
  gasLimit: bn(70935),
  maxFee: bn(69242),
  tip: bn(100),
};

Explanation of Parameters

  • Gas Limit (gasLimit): The maximum amount of gas you're willing to allow the transaction to consume. If the transaction requires more gas than this limit, it will fail.
    • Example: gasLimit: bn(70935)
  • Max Fee (maxFee): The maximum amount you're willing to pay for the transaction using the base asset. This allows users to set an upper limit on the transaction fee they are willing to pay, preventing unexpected high costs due to sudden network congestion or fee spikes.
    • Example: maxFee: bn(69242)
  • Tip (tip): An optional amount of the base asset to incentivize the block producer to include the transaction, ensuring faster processing for those willing to pay more. The value set here will be added to the transaction maxFee.
    • Example: tip: bn(100)

Fuel’s transaction fee model provides a balanced approach to cost and incentivization:

  • Transaction Fees are composed of intrinsic and execution gas fees, with the gasPrice determining the final transaction cost.
  • Tip and max_gas determine transaction priority, allowing users to prioritize their transactions by increasing the tip or adjusting the gas limit.

By setting parameters such as gasLimit, maxFee, tip, and others, users have full control over the cost and priority of their transactions, ensuring a flexible and efficient experience within the Fuel network.

Chapter 3 - Fuel's Future

Fuel Ignition's launch establishes the groundwork for a network of high-performance blockchains and diverse infrastructure, supporting numerous decentralized applications.

Post-mainnet, Fuel will introduce a range of advanced features to elevate blockchain technology.

A Network of Interconnected L2s & L3s

Fuel Ignition is the beach-head network, introducing the Fuel technology to the world. However, as the technology matures and the ecosystem grows, this technology will be used to power a growing ecosystem of modular rollups. Developers choose to launch independent chains either to customize the stack, to capture economic value, or simply to build an independent ecosystem for community building.

Fuel’s technology already enables the fastest execution and lowest fees, and future development will ensure these chains can easily interoperate with other chains throughout the ecosystem. Fuel’s light-clients, shared sequencer, combined with the SNAP finality gadget (described below) allow these chains to reduce their friction.

Decentralized Block Building

Fully decentralizing block production is the final step towards creating rollups that fully embody the important values of decentralized blockchains.

In addition to simply ensuring that blockchains remain permissionless and censorship-resistant, decentralized block-building will also expand the surface area for how applications can be developed.

SNAP Fast Finality Gadget

One of the most well-known limitations of rollups is the long periods of time needed to send messages from the layer-2 back up to the layer-1 chain. Users of optimistic rollups such as Arbitrum and Optimism face a notable drawback: a week-long wait period for asset withdrawals from the network.While ZK-rollups do offer reduced settlement times, the economics of proving mean that proofs (and therefore withdrawals) can still take up to 24 hours to process.

Fuel has proposed a theoretical new mechanism for substantially reducing the finality time of optimistic rollups. While the full details are outlined in a post on ETHResearch, the mechanism can be understood at a high level as using a bonded committee of operators to attest to both the validity of the chain, as well as the state of Ethereum not being censored.

State Rehydration

Typical blockchain applications treat the chain’s state as a large, public, distributed database. These applications write data to the chain in a similar manner to how a Web2 application would write to a cloud-hosted database. However, this imposes a large burden on the network, which must maintain this data on all nodes. The immutable nature of blockchains means this data is generally stored forever.

Fuel aims to address this issue using a technique known as “state rehydration”. This technique uses the blockchain as a system for maintaining consensus over state commitments, as opposed to a system for syncing a large database.

Specifically, this technique takes advantage of the fact that “calldata” and hashing are relatively inexpensive compared to state storage. State rehydration means that a transaction includes all the state data needed for an action, and this state will be validated against a single on-chain hash as a state commitment. In turn, an application will update the state by hashing the new state values, storing this “dehydrated” commitment in state, and emitting the full state in a log so it can be made available off-chain.

Many current parts of Fuel are considered “state rehydration”, such as predicates which require users to provide the account’s bytecode. Furthermore, some applications have taken state rehydration a step further, using UTXOs and predicates to provide further state reductions.

However, Fuel’s roadmap aims to bring state-rehydration to a level where it can power any arbitrary blockchain application. This requires a full integration between users, block-builders, and off-chain indexers, allowing various parties to pay to reconstruct the state in a completed block. This technique will leverage Fuel’s planned decentralized block-building mechanisms.

Data Streaming

While many projects have focused energy on improving the performance of writing data to blockchains, less attention has been paid to optimizing how to read data from a blockchain. In the future, Fuel will radically restructure how data gets propagated from block-producers out to the various end-users of a network.

Current blockchain applications read data from a network by repeatedly “pinging” an RPC node, asking that node to provide the current state of the network and then checking for updates locally. This method of “polling” for new data is extremely inefficient for all parties, placing computational and network burdens on both the client and server. Furthermore, the “pull” nature of this system means that there will always be some extra latency introduced into the transaction. And in the world of finance, time is money.

Fuel aims to flip the data model on its head, creating a push/subscription model of disseminating data across a network. Fuel will enable block-producers to stream every phase of the transaction supply chain from their own servers, out through a network of lightweight relayers, on to end users. This allows users to have fast access to blockchain data without requiring unnecessary “polling”, and allows financial actors to have the fastest access to the financial information they care about.

Conclusion

Thank you for exploring the Fuel Book. We hope it has provided you with a deeper understanding of the motivations, philosophies, and technical innovations that drive our work. Fuel represents more than just a high-performance blockchain solution—it’s a vision for a decentralized future built on speed, security, and scalability.

As you continue your journey with Fuel, remember that our community thrives on collaboration and shared learning. Whether you're a developer, researcher, or blockchain enthusiast, we invite you to engage with our growing ecosystem, contribute your ideas, and help shape the future of decentralized technology.

Glossary

Contract: Primitives allowing for building stateful applications on Fuel, facilitates complex stateful applications like AMMs, Vaults, etc.

Context: Provides policies that determine what features can some running FuelVM bytecode use, for example the ability to call smart contracts, use persistent storage, etc.

Cryptography: The practice of securing information and communications through mathematical techniques, ensuring data confidentiality, integrity, and authenticity in blockchain systems.

Ephemeral scripting: Scripts or code that are temporary and designed for short-term, single-use purposes. These scripts are typically used for tasks that do not require persistent state or long-term execution and are often discarded or removed after they fulfill their function.

Ethereum Virtual Machine (EVM): A decentralized computing environment that executes smart contracts on the Ethereum blockchain.

Finality: The point at which a transaction is considered permanently recorded on the blockchain and cannot be altered or reversed, providing assurance that it is completed.

Forc: A command-line toolchain that serves as the backbone of Fuel development. It supports everything from compiling Sway smart contracts to managing dependencies and deploying applications.

Fuel Ignition: Will be the first Fuel V2 rollup to go live on Ethereum Mainnet. It aims to surpass traditional EVM rollups by delivering a vastly improved execution design.

Fuel Rust SDK: A developer tool that allows developers to interact with Fuel’s blockchain using the Rust programming language. It offers a seamless experience for creating system-level applications and managing interactions with the Fuel Network.

Fuel Typescript SDK: A developer tool that allows developers to integrate Fuel into web applications. It simplifies interaction with the Fuel blockchain, making it easy for frontend developers to build decentralized applications that interact with Fuel’s infrastructure.

Fuel Virtual Machine (FuelVM): A high-performance execution environment for the Fuel Network that enables parallel transaction processing, achieving up to 21,000 transactions per second per core. It optimizes resource use and minimizes demands on full nodes, enhancing network sustainability and decentralization.

Fuel Wallet SDK: A developer tool that provides developers with the tools to create secure, user-friendly wallets that natively interact with the Fuel ecosystem. It ensures developers can easily build wallets that integrate into decentralized applications.

Interoperability: The ability of different networks to communicate and exchange assets or data seamlessly, enabling cross-chain functionality without intermediaries.

Layer 1: A base blockchain network (e.g., Bitcoin, Ethereum) responsible for processing and finalizing transactions directly on its ledger.

Layer 2: An off-chain scaling solution built on top of a Layer 1 blockchain to improve transaction speed and reduce costs while still relying on Layer 1 for security and finality.

Merkle Tree: A data structure used in blockchains to efficiently and securely verify large sets of transactions, by organizing them into a tree-like structure where each node is a cryptographic hash of its children.

Native Assets: Cryptocurrencies or tokens that are built into and exist directly on a blockchain, serving as the primary currency for that network.

Optimistic Rollup: A Layer 2 scaling solution that processes transactions off-chain while assuming they are valid by default, requiring participants to challenge fraudulent transactions within a specific time frame. If no challenges are made, the transactions are considered valid and finalized on the Layer 1 blockchain.

Parallelism: The ability to process multiple transactions simultaneously.

Predicate: A stateless smart account that allows transactions to execute in parallel without conflict.

Proposer-Builder Separation (PBS): Proposer-builder separation (PBS) is an Ethereum concept designed to enhance network scalability and security by splitting block building responsibilities into two distinct roles: block proposers and block builders.

Rollup: A Layer 2 scaling solution that batches multiple transactions into a single one and processes them off-chain, while still ensuring security and finality on the Layer 1 blockchain.

Scalability: The capability of a blockchain to handle an increasing number of transactions or users without compromising performance, security, or decentralization.

Script: Entrypoint for fuel transactions which dictates what happens as part of a Fuel transaction.

State: All the data a blockchain needs to store and maintain.

State Tree: A data structure used in blockchains to represent the current state of all accounts, smart contracts, and their balances. It allows for efficient storage and retrieval of state information, often enabling quick verification and updates during transaction processing.

Sway: a domain specific language (DSL) for modern blockchain programming which has familiar syntax, grammar and design ideology to Rust while incorporating blockchain specific functionality such as smart contract interface concepts.

Throughput: The number of transactions a blockchain can process within a given time frame, often measured in transactions per second (TPS).

Unspent Transaction Output (UTXO): The model used for tracking asset ownership, contracts, messages and transactions.

Virtual Machine (VM): An environment that executes smart contracts on a blockchain, enabling developers to run code in a decentralized manner without needing to interact with the underlying hardware.

Zero Knowledge: A cryptographic method that allows one party to prove to another that they know a value without revealing the value itself or any other information.

Running a Fuel Ignition Node

Below is a summary of important information to help you get started with running a node for the Layer 2 Fuel Ignition blockchain.

For the latest version of the Fuel client, please visit this link.

Understanding Fuel Ignition's Consensus Mechanism

Fuel Ignition operates on a Proof of Authority (PoA) consensus mechanism. Here’s a brief overview:

Validators: In PoA, there are specific entities, known as validators or "authorities", who are given the responsibility to create new blocks and validate transactions. Unlike other consensus mechanisms like Proof of Work (PoW) or Proof of Stake (PoS), where validators are chosen based on computational power or stake, PoA validators are selected based on their reputation and trustworthiness within the network.

Benefits of PoA: PoA provides faster transaction times and requires less computational power, making it more energy-efficient. The security and integrity of the network are maintained by the trustworthiness of the selected validators.

Hardware Requirements

HardwareMinimumRecommended
Processor2 Cores8 Cores
Memory8 GB16 GB
Storage500 GB1 TB

For low API traffic, an AWS m5.large instance should be sufficient. However, we recommend an AWS m5.4xlarge instance to match the configuration we use for running the network.

For routine tasks such as deploying simple contracts and testing contract interactions locally, you do not need to meet all the hardware requirements listed above.

Getting Started

Depending on your requirements, you can choose one of the following setups:

  1. Run a Local Fuel Ignition Node: This setup allows you to run a node that operates solely in your local environment.
  2. Connect to the Fuel Ignition Testnet: With this setup, your local node will connect and sync with Fuel Ignition.
  3. Connect to the Fuel Ignition Mainnet: With this setup, your local node will connect and sync with the Mainnet version of Fuel Ignition.

Running a local Fuel node

In addition to deploying and testing on the Fuel Testnet, you can also run a local Fuel Node.

There are two types of Fuel networks that can be run:

  1. In-memory network (without persistence)
  2. Local network with persistence

Using forc node to run a Local Node

If you wish to still use the fuel-core binary directly, you can skip this section and continue with the steps below.

Make sure you have the latest version of fuelup installed or updated. forc node abstracts all the flags and configuration options of the fuel-core binary and is intended for ease of use. To run a local node using forc, you can use the following command:

forc node local

This command will start a local node with the default configuration (with state persistence). The default configuration is highlighted in green at the top of the command output.

If you want to specify a custom configuration, you can use the --help flag to see the available options. For example:

forc node local --help

Dry-run mode

Users of this new plugin may want to review the parameters before running the node. To accommodate this, forc-node includes a dry-run mode, which can be enabled using:

forc-node --dry-run local

Instead of starting the node, this command will print the exact command that would be run, allowing you to verify the parameters beforehand.

Using fuel-core binary to run a local node

If you wish to still use the fuel-core binary directly, you can follow the steps below.

In-memory local node (without state persistence)

An in-memory node does not persist the blockchain state anywhere, it is only stored in memory as long as the node is active and running.

First ensure your environments open files limit ulimit is increased, example:

ulimit -S -n 32768

After ensuring your file limit is increased, to spin-up a local in-memory Fuel node download or copy the local snapshot from here, then run the following command:

fuel-core run --db-type in-memory --debug --snapshot ./your/path/to/chain_config_folder

To deploy a contract to the local node, run the following command:

forc deploy <signing-key> --node-url 127.0.0.1:4000/v1/graphql

Or to deploy with the default signer that is pre-funded by fuel-core:

forc deploy --default-signer --node-url 127.0.0.1:4000/v1/graphql

Chain Configuration

To modify the initial state of the chain, you must configure the state_config.json file in your chain configuration folder.

For simplicity, clone the repository into the directory of your choice.

When using the --snapshot flag later, you can replace ./your/path/to/chain_config_folder with the local folder of the repository you just cloned ./chain-configuration/local/.

To start the node with a custom configuration, you can use the command below:

fuel-core run --snapshot ./your/path/to/chain_config_folder --db-type in-memory --debug

To find an example local chain configuration folder for a specific fuel-core version, refer to the chain-configuration/local repo.

Funding a wallet locally

You can edit the coins array inside state_config.json to modify the initial assets owned by a given address.

The owner address must be a B256 type address (begins with 0x) instead of a Bech32 type (begins with fuel).

The amount is a numerical value. In the example below, the value translates to 1 ETH.

"coins": [
  {
    "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000001",
    "output_index": 0,
    "tx_pointer_block_height": 0,
    "tx_pointer_tx_idx": 0,
    "owner": "0x488284d46414347c78221d3bad71dfebcff61ab2ae26d71129701d50796f714d",
    "amount": 1000000000,
    "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07"
  }
]

Local node (with state persistence)

This node does persist the blockchain state locally. To run a local node with persistence a chain configuration file is required.

To start the node, run the following command:

fuel-core run --ip 127.0.0.1 --port 4000 --snapshot ./your/path/to/chain_config_folder --db-path ./.fueldb --debug

Connecting to the local node from a browser wallet

To connect to the local node using a browser wallet, import the network address as:

http://127.0.0.1:4000/v1/graphql

Running a local Fuel node connected to Testnet using P2P

Fuel is getting ready for our next major client release, which will upgrade the network from version 0.40.x to 0.41.x. This will be a required upgrade for any node operators. We are targeting February 20 for the testnet upgrade. You can upgrade immediately to the 0.41.6 release and sync with the current network using the latest release. This update includes several improvements, such as database optimizations for some API queries. To fully benefit from these changes, you will need to re-sync the chain from the genesis block. While this isn't required for operation, it is recommended for optimal performance.

Installation

To install the Fuel toolchain, you can use the fuelup-init script. This will install forc, forc-client, forc-fmt, forc-lsp, forc-wallet as well as fuel-core in ~/.fuelup/bin.

curl https://install.fuel.network | sh

Having problems? Visit the installation guide or post your question in our forum.

Getting a Sepolia (Ethereum Testnet) API Key

An API key from any RPC provider that supports the Sepolia network will work. Relayers will help listen to events from the Ethereum network. We recommend either Infura or Alchemy

The endpoints should look like the following:

Infura

https://sepolia.infura.io/v3/{YOUR_API_KEY}

Alchemy

https://eth-sepolia.g.alchemy.com/v2/{YOUR_API_KEY}

Note that using other network endpoints will result in the relayer failing to start.

Using forc node to run a Testnet Node

If you wish to still use the fuel-core binary directly, you can skip this section and continue with the steps below.

Make sure you have the latest version of fuelup installed or updated. forc node abstracts all the flags and configuration options of the fuel-core binary and is intended for ease of use. To run a testnet node using forc, you can use the following command:

forc node testnet

This command will prompt for two things:

  1. You will be asked to create a keypair if you don't already have one.
  2. You will be asked to provide an Ethereum RPC endpoint that you retrieved from the Getting a Sepolia (Ethereum Testnet) API Key section above.

The default configuration is highlighted in green at the top of the command output.

If you want to specify a custom configuration, you can use the --help flag to see the available options. For example:

forc node testnet --help

Dry-run mode

Users of this new plugin may want to review the parameters before running the node. To accommodate this, forc-node includes a dry-run mode, which can be enabled using:

forc-node --dry-run testnet

Instead of starting the node, this command will print the exact command that would be run, allowing you to verify the parameters beforehand.

Using fuel-core binary to run a local node

If you wish to still use the fuel-core binary directly, you can follow the steps below.

Generating a P2P Key

Generate a new P2P key pairing by running the following command:

fuel-core-keygen new --key-type peering
{
  "peer_id":"16Uiu2HAmEtVt2nZjzpXcAH7dkPcFDiL3z7haj6x78Tj659Ri8nrs",
  "secret":"b0ab3227974e06d236d265bd1077bb0522d38ead16c4326a5dff2f30edf88496",
  "type":"peering"
}
### Do not share or lose this private key! Press any key to complete. ###

Make sure you save this somewhere safe so you don't need to generate a new key pair in the future.

Chain Configuration

To run a local node with persistence, you must have a folder with the following chain configuration files:

For simplicity, clone the repository into the directory of your choice.

When using the --snapshot flag later, you can replace ./your/path/to/chain_config_folder with the ignition-test folder of the repository you just cloned ./chain-configuration/ignition-test/.

Running a Local Node

First ensure your environments open files limit ulimit is increased, example:

ulimit -S -n 32768

Finally to put everything together to start the node, run the following command:

fuel-core run \
--service-name=fuel-sepolia-testnet-node \
--keypair {P2P_PRIVATE_KEY} \
--relayer {ETHEREUM_RPC_ENDPOINT} \
--ip=0.0.0.0 --port=4000 --peering-port=30333 \
--db-path=~/.fuel-sepolia-testnet \
--snapshot ./your/path/to/chain_config_folder \
--utxo-validation --poa-instant false --enable-p2p \
--bootstrap-nodes /dnsaddr/testnet.fuel.network \
--sync-header-batch-size 50 \
--enable-relayer \
--relayer-v2-listening-contracts=0x01855B78C1f8868DE70e84507ec735983bf262dA \
--relayer-da-deploy-height=5827607 \
--relayer-log-page-size=500 \
--sync-block-stream-buffer-size 30

For the full description details of each flag above, run:

fuel-core run --help

Connecting to the local node from a browser wallet

To connect to the local node using a browser wallet, import the network address as:

http://0.0.0.0:4000/v1/graphql

Running a local Fuel node connected to Mainnet using P2P

Fuel is preparing for the next major client release, upgrading the network from version 0.40.x to 0.41.x. This will be a required upgrade for all node operators. The mainnet upgrade is scheduled for March 6. You can upgrade immediately to the 0.41.6 release and sync with the current network using the latest release. This release brings database optimizations for some API queries. To take full advantage of these improvements, we recommend re-syncing the chain from the genesis block. While not mandatory, doing so will ensure the best performance.

Installation

To install the Fuel toolchain, you can use the fuelup-init script. This will install forc, forc-client, forc-fmt, forc-lsp, forc-wallet as well as fuel-core in ~/.fuelup/bin.

curl https://install.fuel.network | sh

Having problems? Visit the installation guide or post your question in our forum.

Getting a mainnet Ethereum API Key

An API key from any RPC provider that supports the Sepolia network will work. Relayers will help listen to events from the Ethereum network. We recommend either Infura or Alchemy

The endpoints should look like the following:

Infura

https://mainnet.infura.io/v3/{YOUR_API_KEY}

Alchemy

https://eth-mainnet.g.alchemy.com/v2/{YOUR_API_KEY}

Note that using other network endpoints will result in the relayer failing to start.

Using forc node to run a Mainnet Node

If you wish to still use the fuel-core binary directly, you can skip this section and continue with the steps below.

Make sure you have the latest version of fuelup installed or updated. forc node abstracts all the flags and configuration options of the fuel-core binary and is intended for ease of use. To run a mainnet node using forc, you can use the following command:

forc node ignition

This command will prompt for two things:

  1. You will be asked to create a keypair if you don't already have one.
  2. You will be asked to provide an Ethereum RPC endpoint that you retrieved from the Getting a mainnet Ethereum API Key section above.

The default configuration is highlighted in green at the top of the command output.

If you want to specify a custom configuration, you can use the --help flag to see the available options. For example:

forc node ignition --help

Dry-run mode

Users of this new plugin may want to review the parameters before running the node. To accommodate this, forc-node includes a dry-run mode, which can be enabled using:

forc-node --dry-run ignition

Instead of starting the node, this command will print the exact command that would be run, allowing you to verify the parameters beforehand.

Using fuel-core binary to run a local node

If you wish to still use the fuel-core binary directly, you can follow the steps below.

Generating a P2P Key

Generate a new P2P key pairing by running the following command:

fuel-core-keygen new --key-type peering
{
  "peer_id":"16Uiu2HAmEtVt2nZjzpXcAH7dkPcFDiL3z7haj6x78Tj659Ri8nrs",
  "secret":"b0ab3227974e06d236d265bd1077bb0522d38ead16c4326a5dff2f30edf88496",
  "type":"peering"
}
### Do not share or lose this private key! Press any key to complete. ###

Make sure you save this somewhere safe so you don't need to generate a new key pair in the future.

Chain Configuration

To run a local node with persistence, you must have a folder with the following chain configuration files:

For simplicity, clone the repository into the directory of your choice.

When using the --snapshot flag later, you can replace ./your/path/to/chain_config_folder with the ignition folder of the repository you just cloned ./chain-configuration/ignition/.

Running a Local Node

First ensure your environments open files limit ulimit is increased, example:

ulimit -S -n 32768

Please make sure you have the latest version of the Fuel toolchain installed and properly configured before continuing.

Finally to put everything together to start the node, run the following command:

fuel-core run \
--enable-relayer \
--service-name fuel-mainnet-node \
--keypair {P2P_PRIVATE_KEY} \
--relayer {ETHEREUM_RPC_ENDPOINT} \
--ip=0.0.0.0 --port 4000 --peering-port 30333 \
--db-path ~/.fuel-mainnet \
--snapshot ./your/path/to/chain_config_folder \
--utxo-validation --poa-instant false --enable-p2p \
--bootstrap-nodes /dnsaddr/mainnet.fuel.network \
--sync-header-batch-size 50 \
--relayer-v2-listening-contracts=0xAEB0c00D0125A8a788956ade4f4F12Ead9f65DDf \
--relayer-da-deploy-height=20620434 \
--relayer-log-page-size=100 \
--sync-block-stream-buffer-size 30

For the full description details of each flag above, run:

fuel-core run --help

Connecting to the local node from a browser wallet

To connect to the local node using a browser wallet, import the network address as:

http://0.0.0.0:4000/v1/graphql

Running a Fuel Sequencer Node or Validator

Below is a summary of key information to help you get started with running a node for the Fuel Sequencer blockchain.

For more details, please refer to the deployment repository.

Hardware Requirements

HardwareMinimumRecommended
Processor4 Cores8 Cores
Memory8 GB16 GB
Storage200 GB1 TB

Port Configuration

Unless otherwise configured, the following ports should be available:

  • Sequencer: 26656, 26657, 9090, 1317
  • Sidecar: 8080
  • Ethereum: 8545, 8546

These components interact with each other, so any changes to the port configuration must be reflected in the corresponding components. Specifically:

  • Changes to Sequencer ports must be updated in the Sidecar's runtime flags.
  • Changes to the Sidecar port must be updated in the Sequencer's app config.
  • Changes to Ethereum ports must be updated in the Sidecar's runtime flags.

Getting Started

Depending on your needs, you can choose one of the following setups:

  1. Running a Mainnet Fuel Sequencer Node: Your local node will connect to and sync with the mainnet Fuel Sequencer network.
  2. Running a Mainnet Fuel Sequencer Validator: Your local node will connect to, sync with, and validate transactions on the mainnet Fuel Sequencer network.
  3. Running a Testnet Fuel Sequencer Node: Your local node will connect to and sync with the testnet Fuel Sequencer network.
  4. Running a Testnet Fuel Sequencer Validator: Your local node will connect to, sync with, and validate transactions on the testnet Fuel Sequencer network.

Run Sequencer Node

Prerequisites

This guide assumes that Golang is installed to run Cosmovisor. We recommend using version 1.21 or later. You can download it here.

Run the Node

Obtain binary and genesis from this repository:

  • Binary from: https://github.com/FuelLabs/fuel-sequencer-deployments/releases/tag/seq-testnet-2-improved-sidecar
    • For example:
      • fuelsequencerd-seq-testnet-2-improved-sidecar-darwin-arm64 for Apple Silicon
      • fuelsequencerd-seq-testnet-2-improved-sidecar-darwin-amd64 for Linux x64
  • Genesis from: https://github.com/FuelLabs/fuel-sequencer-deployments/blob/main/seq-testnet-2/genesis.json

Download the right binary based on your architecture to $GOPATH/bin/ with the name fuelsequencerd:

  • echo $GOPATH to ensure it exists. If not, go might not be installed.
  • Make sure that your GOPATH is set properly in your .bashrc or .zshrc file. Run source ~/.bashrc or source ~/.zshrc to apply the changes.
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
  • mkdir $GOPATH/bin/ if the directory does not exist.
  • wget <url/to/binary> to download the binary, or any equivalent approach. For example:
wget https://github.com/FuelLabs/fuel-sequencer-deployments/releases/download/seq-testnet-2-improved-sidecar/fuelsequencerd-seq-testnet-2-improved-sidecar-darwin-arm64
  • cp <binary> $GOPATH/bin/fuelsequencerd to copy the binary to the GOPATH/bin/ directory.
  • chmod +x $GOPATH/bin/fuelsequencerd to make the binary executable.
  • fuelsequencerd version to verify that the binary is working.

Try the binary:

fuelsequencerd version  # expect seq-testnet-2-improved-sidecar

Initialise node directory:

fuelsequencerd init <node-name> --chain-id seq-testnet-2

Copy the downloaded genesis file to ~/.fuelsequencer/config/genesis.json:

cp <path/to/genesis.json> ~/.fuelsequencer/config/genesis.json

Configure the node (part 1: ~/.fuelsequencer/config/app.toml):

  • Set minimum-gas-prices = "10test".
  • Configure [sidecar]:
    • Ensure that enabled = false.

Configure the node (part 2: ~/.fuelsequencer/config/config.toml):

  • Configure [p2p]:
    • Set persistent_peers = "3a0b4118c01addd33d5add81783805d5add2fb17@80.64.208.17:26656".
  • Configure [mempool]:
    • Set max_tx_bytes = 1153434 (1.1MiB)
    • Set max_txs_bytes = 23068670 (~22MiB)
  • Configure [rpc]:
    • Set max_body_bytes = 1153434 (optional - relevant for public RPC).

Note: Ensuring consistent CometBFT mempool parameters across all network nodes is important to reduce transaction delays. This includes mempool.size, mempool.max_txs_bytes, and mempool.max_tx_bytes in config.toml and minimum-gas-prices in app.toml, as pointed out above.

Install Cosmovisor

To install Cosmovisor, run go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@latest

Set the environment variables:

If you're running on a zsh terminal...
echo "# Setup Cosmovisor" >> ~/.zshrc
echo "export DAEMON_NAME=fuelsequencerd" >> ~/.zshrc
echo "export DAEMON_HOME=$HOME/.fuelsequencer" >> ~/.zshrc
echo "export DAEMON_ALLOW_DOWNLOAD_BINARIES=true" >> ~/.zshrc
echo "export DAEMON_LOG_BUFFER_SIZE=512" >> ~/.zshrc
echo "export DAEMON_RESTART_AFTER_UPGRADE=true" >> ~/.zshrc
echo "export UNSAFE_SKIP_BACKUP=true" >> ~/.zshrc
echo "export DAEMON_SHUTDOWN_GRACE=15s" >> ~/.zshrc

# You can check https://docs.cosmos.network/main/tooling/cosmovisor for more configuration options.

Apply to your current session: source ~/.zshrc

If you're running on a bash terminal...
echo "# Setup Cosmovisor" >> ~/.bashrc
echo "export DAEMON_NAME=fuelsequencerd" >> ~/.bashrc
echo "export DAEMON_HOME=$HOME/.fuelsequencer" >> ~/.bashrc
echo "export DAEMON_ALLOW_DOWNLOAD_BINARIES=true" >> ~/.bashrc
echo "export DAEMON_LOG_BUFFER_SIZE=512" >> ~/.bashrc
echo "export DAEMON_RESTART_AFTER_UPGRADE=true" >> ~/.bashrc
echo "export UNSAFE_SKIP_BACKUP=true" >> ~/.bashrc
echo "export DAEMON_SHUTDOWN_GRACE=15s" >> ~/.bashrc

# You can check https://docs.cosmos.network/main/tooling/cosmovisor for more configuration options.

Apply to your current session: source ~/.bashrc

You can now test that cosmovisor was installed properly:

cosmovisor version

Initialise Cosmovisor directories (hint: whereis fuelsequencerd for the path):

cosmovisor init <path/to/fuelsequencerd>

At this point cosmovisor run will be the equivalent of running fuelsequencerd, however you should not run the node for now.

Configure State Sync

State Sync allows a node to get synced up quickly.

To configure State Sync, you will need to set these values in ~/.fuelsequencer/config/config.toml under [statesync]:

  • enable = true to enable State Sync
  • rpc_servers = ...
  • trust_height = ...
  • trust_hash = ...

The last three values can be obtained from the explorer.

You will need to specify at least two comma-separated RPC servers in rpc_servers. You can either refer to the list of alternate RPC servers above or use the same one twice.

Running the Sequencer

At this point you should already be able to run cosmovisor run start to run the Sequencer. However, it is highly recommended to run the Sequencer as a background service.

Some examples are provided below for Linux and Mac. You will need to replicate the environment variables defined when setting up Cosmovisor.

Linux

On Linux, you can use systemd to run the Sequencer in the background. Knowledge of how to use systemd is assumed here.

Here's an example service file with some placeholder (<...>) values that must be filled-in:

Click me...
[Unit]
Description=Sequencer Node
After=network.target

[Service]
Type=simple
User=<USER>
ExecStart=/home/<USER>/go/bin/cosmovisor run start
Restart=on-failure
RestartSec=3
LimitNOFILE=4096

Environment="DAEMON_NAME=fuelsequencerd"
Environment="DAEMON_HOME=/home/<USER>/.fuelsequencer"
Environment="DAEMON_ALLOW_DOWNLOAD_BINARIES=true"
Environment="DAEMON_LOG_BUFFER_SIZE=512"
Environment="DAEMON_RESTART_AFTER_UPGRADE=true"
Environment="UNSAFE_SKIP_BACKUP=true"
Environment="DAEMON_SHUTDOWN_GRACE=15s"

[Install]
WantedBy=multi-user.target

Mac

On Mac, you can use launchd to run the Sequencer in the background. Knowledge of how to use launchd is assumed here.

Here's an example plist file with some placeholder ([...]) values that must be filled-in:

Click me...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>fuel.sequencer</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/[User]/go/bin/cosmovisor</string>
        <string>run</string>
        <string>start</string>
    </array>

    <key>UserName</key>
    <string>[User]</string>

    <key>EnvironmentVariables</key>
    <dict>
        <key>DAEMON_NAME</key>
        <string>fuelsequencerd</string>
        <key>DAEMON_HOME</key>
        <string>/Users/[User]/.fuelsequencer</string>
        <key>DAEMON_ALLOW_DOWNLOAD_BINARIES</key>
        <string>true</string>
        <key>DAEMON_LOG_BUFFER_SIZE</key>
        <string>512</string>
        <key>DAEMON_RESTART_AFTER_UPGRADE</key>
        <string>true</string>
        <key>UNSAFE_SKIP_BACKUP</key>
        <string>true</string>
        <key>DAEMON_SHUTDOWN_GRACE</key>
        <string>15s</string>
    </dict>

    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>

    <key>HardResourceLimits</key>
    <dict>
        <key>NumberOfFiles</key>
        <integer>4096</integer>
    </dict>

    <key>StandardOutPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sequencer.out</string>
    <key>StandardErrorPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sequencer.err</string>
</dict>
</plist>

References

Based on material from:

  • https://docs.cosmos.network/main/tooling/cosmovisor
  • https://docs.osmosis.zone/overview/validate/joining-mainnet#set-up-cosmovisor

Run Sequencer Validator

Typical Setup

The validator setup will consist of a Fuel Sequencer, Sidecar, and a connection to an Ethereum Sepolia Node.

fuel sequencer validator

Prerequisites

This guide assumes that Golang is installed to run Cosmovisor. We recommend using version 1.21 or later. You can download it here.

Run an Ethereum Sepolia Full Node

To ensure the highest performance and reliability of the Sequencer infrastructure, running your own Ethereum Sepolia full node is a requirement. Avoiding the use of third-party services for Ethereum node operations significantly helps the Sequencer network's liveness. Please note these recommended node configurations:

--syncmode=snap
--gcmode=full

Configure the Sequencer

Obtain binary and genesis from this repository:

  • Binary from: https://github.com/FuelLabs/fuel-sequencer-deployments/releases/tag/seq-testnet-2-improved-sidecar
    • For example:
      • fuelsequencerd-seq-testnet-2-improved-sidecar-darwin-arm64 for Apple Silicon
      • fuelsequencerd-seq-testnet-2-improved-sidecar-darwin-amd64 for Linux x64
  • Genesis from: https://github.com/FuelLabs/fuel-sequencer-deployments/blob/main/seq-testnet-2/genesis.json

Download the right binary based on your architecture to $GOPATH/bin/ with the name fuelsequencerd:

  • echo $GOPATH to ensure it exists. If not, go might not be installed.
  • Make sure that your GOPATH is set properly in your .bashrc or .zshrc file. Run source ~/.bashrc or source ~/.zshrc to apply the changes.
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
  • mkdir $GOPATH/bin/ if the directory does not exist.
  • wget <url/to/binary> to download the binary, or any equivalent approach. For example:
wget https://github.com/FuelLabs/fuel-sequencer-deployments/releases/download/seq-testnet-2-improved-sidecar/fuelsequencerd-seq-testnet-2-improved-sidecar-darwin-arm64
  • cp <binary> $GOPATH/bin/fuelsequencerd to copy the binary to the GOPATH/bin/ directory.
  • chmod +x $GOPATH/bin/fuelsequencerd to make the binary executable.
  • fuelsequencerd version to verify that the binary is working.

Try the binary:

fuelsequencerd version  # expect seq-testnet-2-improved-sidecar

Initialise the node directory, giving your node a meaningful name:

fuelsequencerd init <node-name> --chain-id seq-testnet-2

Copy the downloaded genesis file to ~/.fuelsequencer/config/genesis.json:

cp <path/to/genesis.json> ~/.fuelsequencer/config/genesis.json

Configure the node (part 1: ~/.fuelsequencer/config/app.toml):

  • Set minimum-gas-prices = "10test".
  • Configure [sidecar]:
    • Ensure that enabled = true.
    • Ensure that address is where the Sidecar will run.
  • Configure [api]:
    • Set swagger=true (optional).
    • Set rpc-max-body-bytes = 1153434 (optional - relevant for public REST).
  • Configure [commitments]:
    • Set api-enabled = true (optional - relevant for public REST).
  • Configure [state-sync]:
    • Set snapshot-interval = 1000 (optional - to provide state-sync service).
  • Configure:
    • Set rpc-read-timeout = 10 (optional - relevant for public REST).
    • Set rpc-write-timeout = 0 (optional - relevant for public REST).

WARNING: leaving the [commitments] API accessible to anyone can lead to DoS! It is highly recommended to handle whitelisting or authentication by a reverse proxy like Traefik for gRPC if the commitments API is enabled.

Configure the node (part 2: ~/.fuelsequencer/config/config.toml):

  • Configure [p2p]:
    • Set persistent_peers = "fc5fd264190e4a78612ec589994646268b81f14e@80.64.208.207:26656".
  • Configure [mempool]:
    • Set max_tx_bytes = 1258291 (1.2MiB)
    • Set max_txs_bytes = 23068672 (22MiB)
  • Configure [rpc]:
    • Set max_body_bytes = 1153434 (optional - relevant for public RPC).

Note: Ensuring consistent CometBFT mempool parameters across all network nodes is important to reduce transaction delays. This includes mempool.size, mempool.max_txs_bytes, and mempool.max_tx_bytes in config.toml and minimum-gas-prices in app.toml, as pointed out above.

Install Cosmovisor

To install Cosmovisor, run go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@latest

Set the environment variables:

If you're running on a zsh terminal...
echo "# Setup Cosmovisor" >> ~/.zshrc
echo "export DAEMON_NAME=fuelsequencerd" >> ~/.zshrc
echo "export DAEMON_HOME=$HOME/.fuelsequencer" >> ~/.zshrc
echo "export DAEMON_ALLOW_DOWNLOAD_BINARIES=true" >> ~/.zshrc
echo "export DAEMON_LOG_BUFFER_SIZE=512" >> ~/.zshrc
echo "export DAEMON_RESTART_AFTER_UPGRADE=true" >> ~/.zshrc
echo "export UNSAFE_SKIP_BACKUP=true" >> ~/.zshrc
echo "export DAEMON_SHUTDOWN_GRACE=15s" >> ~/.zshrc

# You can check https://docs.cosmos.network/main/tooling/cosmovisor for more configuration options.

Apply to your current session: source ~/.zshrc

If you're running on a bash terminal...
echo "# Setup Cosmovisor" >> ~/.bashrc
echo "export DAEMON_NAME=fuelsequencerd" >> ~/.bashrc
echo "export DAEMON_HOME=$HOME/.fuelsequencer" >> ~/.bashrc
echo "export DAEMON_ALLOW_DOWNLOAD_BINARIES=true" >> ~/.bashrc
echo "export DAEMON_LOG_BUFFER_SIZE=512" >> ~/.bashrc
echo "export DAEMON_RESTART_AFTER_UPGRADE=true" >> ~/.bashrc
echo "export UNSAFE_SKIP_BACKUP=true" >> ~/.bashrc
echo "export DAEMON_SHUTDOWN_GRACE=15s" >> ~/.bashrc

# You can check https://docs.cosmos.network/main/tooling/cosmovisor for more configuration options.

Apply to your current session: source ~/.bashrc

You can now test that cosmovisor was installed properly:

cosmovisor version

Initialise Cosmovisor directories (hint: whereis fuelsequencerd for the path):

cosmovisor init <path/to/fuelsequencerd>

At this point cosmovisor run will be the equivalent of running fuelsequencerd, however you should not run the node for now.

Configure State Sync

State Sync allows a node to get synced up quickly.

To configure State Sync, you will need to set these values in ~/.fuelsequencer/config/config.toml under [statesync]:

  • enable = true to enable State Sync
  • rpc_servers = ...
  • trust_height = ...
  • trust_hash = ...

The last three values can be obtained from the explorer.

You will need to specify at least two comma-separated RPC servers in rpc_servers. You can either refer to the list of alternate RPC servers above or use the same one twice.

Run the Sidecar

At this point you should already be able to run fuelsequencerd start-sidecar with the right flags, to run the Sidecar. However, it is highly recommended to run the Sidecar as a background service.

It is also very important to ensure that you provide all the necessary flags when running the Sidecar to ensure that it can connect to an Ethereum node and to the Sequencer node, and is also accessible by the Sequencer node. The most important flags are:

  • host: host for the gRPC server to listen on
  • port: port for the gRPC server to listen on
  • eth_ws_url: Ethereum node WebSocket endpoint
  • eth_rpc_url: Ethereum node RPC endpoint
  • eth_contract_address: address in hex format of the contract to monitor for logs. This MUST be set to 0x0E5CAcD6899a1E2a4B4E6e0c8a1eA7feAD3E25eD.
  • sequencer_grpc_url: Sequencer node gRPC endpoint

Linux

On Linux, you can use systemd to run the Sequencer in the background. Knowledge of how to use systemd is assumed here.

Here's an example service file with some placeholder (<...>) values that must be filled-in:

Click me...
[Unit]
Description=Sidecar
After=network.target

[Service]
Type=simple
User=<USER>
ExecStart=<HOME>/go/bin/fuelsequencerd start-sidecar \
    --host "0.0.0.0" \
    --sequencer_grpc_url "127.0.0.1:9090" \
    --eth_ws_url "<ETHEREUM_NODE_WS>" \
    --eth_rpc_url "<ETHEREUM_NODE_RPC>" \
    --eth_contract_address "0x0E5CAcD6899a1E2a4B4E6e0c8a1eA7feAD3E25eD"
Restart=on-failure
RestartSec=3
LimitNOFILE=4096

[Install]
WantedBy=multi-user.target

Mac

On Mac, you can use launchd to run the Sequencer in the background. Knowledge of how to use launchd is assumed here.

Here's an example plist file with some placeholder ([...]) values that must be filled-in:

Click me...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>fuel.sidecar</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/[User]/go/bin/fuelsequencerd</string>
        <string>start-sidecar</string>
        <string>--host</string>
        <string>0.0.0.0</string>
        <string>--sequencer_grpc_url</string>
        <string>127.0.0.1:9090</string>
        <string>--eth_ws_url</string>
        <string>[ETHEREUM_NODE_WS]</string>
        <string>--eth_rpc_url</string>
        <string>[ETHEREUM_NODE_RPC]</string>
        <string>--eth_contract_address</string>
        <string>0x0E5CAcD6899a1E2a4B4E6e0c8a1eA7feAD3E25eD</string>
    </array>

    <key>UserName</key>
    <string>[User]</string>

    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>

    <key>HardResourceLimits</key>
    <dict>
        <key>NumberOfFiles</key>
        <integer>4096</integer>
    </dict>

    <key>StandardOutPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sidecar.out</string>
    <key>StandardErrorPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sidecar.err</string>
</dict>
</plist>

Run the Sequencer

At this point you should already be able to run cosmovisor run start to run the Sequencer. However, it is highly recommended to run the Sequencer as a background service.

Some examples are provided below for Linux and Mac. You will need to replicate the environment variables defined when setting up Cosmovisor.

Linux

On Linux, you can use systemd to run the Sequencer in the background. Knowledge of how to use systemd is assumed here.

Here's an example service file with some placeholder (<...>) values that must be filled-in:

Click me...
[Unit]
Description=Sequencer Node
After=network.target

[Service]
Type=simple
User=<USER>
ExecStart=/home/<USER>/go/bin/cosmovisor run start
Restart=on-failure
RestartSec=3
LimitNOFILE=4096

Environment="DAEMON_NAME=fuelsequencerd"
Environment="DAEMON_HOME=/home/<USER>/.fuelsequencer"
Environment="DAEMON_ALLOW_DOWNLOAD_BINARIES=true"
Environment="DAEMON_LOG_BUFFER_SIZE=512"
Environment="DAEMON_RESTART_AFTER_UPGRADE=true"
Environment="UNSAFE_SKIP_BACKUP=true"
Environment="DAEMON_SHUTDOWN_GRACE=15s"

[Install]
WantedBy=multi-user.target

Mac

On Mac, you can use launchd to run the Sequencer in the background. Knowledge of how to use launchd is assumed here.

Here's an example plist file with some placeholder ([...]) values that must be filled-in:

Click me...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>fuel.sequencer</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/[User]/go/bin/cosmovisor</string>
        <string>run</string>
        <string>start</string>
    </array>

    <key>UserName</key>
    <string>[User]</string>

    <key>EnvironmentVariables</key>
    <dict>
        <key>DAEMON_NAME</key>
        <string>fuelsequencerd</string>
        <key>DAEMON_HOME</key>
        <string>/Users/[User]/.fuelsequencer</string>
        <key>DAEMON_ALLOW_DOWNLOAD_BINARIES</key>
        <string>true</string>
        <key>DAEMON_LOG_BUFFER_SIZE</key>
        <string>512</string>
        <key>DAEMON_RESTART_AFTER_UPGRADE</key>
        <string>true</string>
        <key>UNSAFE_SKIP_BACKUP</key>
        <string>true</string>
        <key>DAEMON_SHUTDOWN_GRACE</key>
        <string>15s</string>
    </dict>

    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>

    <key>HardResourceLimits</key>
    <dict>
        <key>NumberOfFiles</key>
        <integer>4096</integer>
    </dict>

    <key>StandardOutPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sequencer.out</string>
    <key>StandardErrorPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sequencer.err</string>
</dict>
</plist>

Creating an Account

To run a validator, you will need to have a Sequencer account address. Generate an address with a key name:

fuelsequencerd keys add <NAME> # for a brand new key

# or

fuelsequencerd keys add <NAME> --recover # to create from a mnemonic

This will give you an output with an address (e.g. fuelsequencer1l7qk9umswg65av0zygyymgx5yg0fx4g0dpp2tl) and a private mnemonic, if you generated a brand new key. Store the mnemonic safely.

Fuel Sequencer addresses also have an Ethereum-compatible (i.e. hex) format. To generate the hex address corresponding to your Sequencer address, run the following:

fuelsequencerd keys parse <ADDRESS>

This will give an output in this form:

bytes: FF8162F37072354EB1E222084DA0D4221E93550F
human: fuelsequencer

Adding the 0x prefix to the address in the first line gives you your Ethereum-compatible address, used to deposit into and interact with your Sequencer address from Ethereum. In this case, it's 0xFF8162F37072354EB1E222084DA0D4221E93550F.

Funding the Account

Ensure your Ethereum account (EOA) has sufficient ETH to cover gas fees.

Important Addresses

Token Faucet

To obtain testnet tokens, visit Fuel's official Ethereum testnet staking UI with any Ethereum EOA that has not previously received FUEL tokens from the faucet.

Click "Faucet Fuel Token" to receive 100 FUEL tokens for testing.

Testnet Fuel Faucet

Token Approval

Before proceeding, you must approve the Fuel token contract to allow the transfer of tokens.

In the Fuel Token Etherscan contract UI, use the approve (0x095ea7b3) function:

  • Spender (address): Set this to the Sequencer Interface (Bridge) address: 0x742C478a1951257E83d3aC8f3DFB3A8e6AB9a2E4.

  • Value (uint256): Enter the number of tokens to approve, including 9 additional decimal places. For unlimited approval, use:

    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    

Testnet Etherscan Approval UI

Bridging Tokens

To bridge tokens, connect your Ethereum wallet by clicking "Connect to Web3" in the top left. Then, use the depositFor (0x36efd6f) function to fund your sequencer account.

Transfer your FUEL tokens using the Sequencer Interface (Bridge) Etherscan UI.

Testnet Etherscan UI

  • Amount (uint256): Enter the number of tokens to send, including 9 additional decimal places.
  • Recipient address: Enter the Ethereum-compatible address you generated earlier (e.g., 0xFF8162F37072354EB1E222084DA0D4221E93550F).

Click "Write" to confirm the transaction. The transfer may take ~20 minutes to process.

Verifying Funds

To verify your funds, enter your sequencer account address (i.e. fuelsequencer1l7qk9umswg65av0zygyymgx5yg0fx4g0dpp2tl) in the testnet block explorer.

Testnet Block Explorer

⚠ WARNING: Always test with a small transfer before bridging FUEL tokens.

Withdrawals

Withdrawals can be easily initiated through the CLI and will be settled on Sepolia approximately 3 days later, as the commitment and bridge finalizations must be completed first.

Identify the account from which you wish to withdraw. Use the following command to list all previously created account names matching your account address above:

fuelsequencerd keys list

Example output:

address: fuelsequencer1zzu4804kp6m6whzza6r75g7mnme2ahqkjuw4kf
  name: my-testnet-validator
  pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Al6W+Ttrscm/8njeMOt79T0BOdphfWGXrDLij+O3g19N"}'
  type: local

Verify that this is the correct address and account name from which you wish to withdraw.

To initiate the withdrawal, use the following command where <eth-destination-address> is any Sepolia address you wish to withdraw to and <amount-in-fuel> is the amount of TEST (FUEL) you wish to withdraw:

Note: The amount in TEST (FUEL) must include 9 decimal places.

fuelsequencerd tx bridge withdraw-to-ethereum <eth-destination-address> <amount-in-fuel> \
  --from=<key> \
  --gas-prices=10test \
  --gas=auto \
  --gas-adjustment 1.5 \
  --node="https://testnet-rpc-fuel-seq.simplystaking.xyz/" \
  --chain-id="seq-testnet-2"

For example:

fuelsequencerd tx bridge withdraw-to-ethereum 0xd70080dE4535db4A64798a23619Db64fB28fD079 1test \
    --from=my-testnet-validator \
    --gas-prices=10test \
    --gas=auto \
    --gas-adjustment 1.5 \
    --node="https://testnet-rpc-fuel-seq.simplystaking.xyz/" \
    --chain-id="seq-testnet-2"

Review the transaction details and confirm the transaction by typing yes when prompted:

gas estimate: 106713
auth_info:
  fee:
    amount:
    - amount: "1067130"
      denom: test
    gas_limit: "106713"
    granter: ""
    payer: ""
  signer_infos: []
  tip: null
body:
  extension_options: []
  memo: ""
  messages:
  - '@type': /fuelsequencer.bridge.v1.MsgWithdrawToEthereum
    amount:
      amount: "1"
      denom: test
    from: fuelsequencer1zzu4804kp6m6whzza6r75g7mnme2ahqkjuw4kf
    to: 0xd70080dE4535db4A64798a23619Db64fB28fD079
  non_critical_extension_options: []
  timeout_height: "0"
signatures: []
confirm transaction before signing and broadcasting [y/N]: 

If the transaction is successful, you will receive a transaction hash, which you can paste and monitor the status of your withdrawal here:

code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: ""
timestamp: ""
tx: null
txhash: FF51288FB916CEE4538E17FB70E438278143FAD2B613D98362A562B02C07253F

Block Explorer Withdrawal

After verifying your withdrawal on the shared sequencer explorer, visit Simply Staking and connect your wallet. Navigate to the Withdrawal tab on the right to monitor the progress of your withdrawal.

Simply Staking Withdrawal

Once the 3-day waiting period has passed, the withdrawal will require manual action to pull the funds out.

Create the Validator

To create the validator, a prerequisite is to have at least 1TEST, with enough extra to pay for gas fees. You can check your balance from the explorer.

Once you have TEST tokens, run the following to create a validator, using the name of the account that you created in the previous steps:

fuelsequencerd tx staking create-validator path/to/validator.json \
    --from <NAME> \
    --gas auto \
    --gas-prices 10test \
    --gas-adjustment 1.5 \
    --chain-id seq-testnet-2

...where validator.json contains:

{
 "pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"<PUBKEY>"},
 "amount": "1000000000test",
 "moniker": "<MONIKER>",
 "identity": "<OPTIONAL-IDENTITY>",
 "website": "<OPTIONAL-WEBSITE>",
 "security": "<OPTIONAL-EMAIL>",
 "details": "<OPTIONAL-DETAILS>",
 "commission-rate": "0.05",
 "commission-max-rate": "<MAX-RATE>",
 "commission-max-change-rate": "<MAX-CHANGE-RATE>",
 "min-self-delegation": "1"
}

...where the pubkey can be obtained using fuelsequencerd tendermint show-validator.

What to Expect

  • The Sequencer should show block syncing.
  • The Sidecar should show block extraction. Occasionally it also receives requests for events.

Tendermint KMS

If you will be using tmkms, make sure that in the config:

  • Chain ID is set to seq-testnet-2 wherever applicable
  • account_key_prefix = "fuelsequencerpub"
  • consensus_key_prefix = "fuelsequencervalconspub"
  • sign_extensions = true
  • protocol_version = "v0.34"

Additional Advanced Configuration

Sidecar flags:

  • development: starts the sidecar in development mode.
  • eth_max_block_range: max number of Ethereum blocks queried at one go.
  • eth_min_logs_query_interval: minimum wait between successive queries for logs.
  • unsafe_eth_start_block: the Ethereum block to start querying from.
  • unsafe_eth_end_block: the last Ethereum block to query. Incorrect use can cause the validator to propose empty blocks, leading to slashing!
  • sequencer_path_to_cert_file: path to the certificate file of the Sequencer infrastructure for secure communication. Specify this value if the Sequencer infrastructure was set up using TLS.
  • sidecar_path_to_cert_file: path to the certificate file of the sidecar server for secure communication. Specify this value if you want to set up a sidecar server with TLS.
  • sidecar_path_to_key_file: path to the private key file of the sidecar server for secure communication. Specify this value if you want to set up a sidecar server with TLS.
  • prometheus_enabled: enables serving of prometheus metrics.
  • prometheus_listen_address: address to listen for prometheus collectors (default ":8081").
  • prometheus_max_open_connections: max number of simultaneous connections (default 3).
  • prometheus_namespace: instrumentation namespace (default "sidecar").
  • prometheus_read_header_timeout: amount of time allowed to read request headers (default 10s).
  • prometheus_write_timeout: maximum duration before timing out writes of the response (default 10s).

Sidecar client flags:

  • sidecar_grpc_url: the sidecar's gRPC endpoint.
  • query_timeout: how long to wait before the request times out.

References

Based on material from:

  • https://docs.cosmos.network/main/tooling/cosmovisor
  • https://docs.osmosis.zone/overview/validate/joining-testnet#set-up-cosmovisor

Run Sequencer Node

Prerequisites

This guide assumes that Golang is installed to run Cosmovisor. We recommend using version 1.21 or later. You can download it here.

Run the Node

Obtain binary and genesis from this repository:

  • Binary from: https://github.com/FuelLabs/fuel-sequencer-deployments/releases/tag/seq-mainnet-1.2-improved-sidecar
    • For example:
      • fuelsequencerd-seq-mainnet-1.2-improved-sidecar-darwin-arm64 for Apple Silicon
      • fuelsequencerd-seq-mainnet-1.2-improved-sidecar-darwin-amd64 for Linux x64
  • Genesis from: https://github.com/FuelLabs/fuel-sequencer-deployments/blob/main/seq-mainnet-1/genesis.json

Download the right binary based on your architecture to $GOPATH/bin/ with the name fuelsequencerd:

  • echo $GOPATH to ensure it exists. If not, go might not be installed.
  • Make sure that your GOPATH is set properly in your .bashrc or .zshrc file. Run source ~/.bashrc or source ~/.zshrc to apply the changes.
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
  • mkdir $GOPATH/bin/ if the directory does not exist.
  • wget <url/to/binary> to download the binary, or any equivalent approach. For example:
wget https://github.com/FuelLabs/fuel-sequencer-deployments/releases/download/seq-mainnet-1.2-improved-sidecar/fuelsequencerd-seq-mainnet-1.2-improved-sidecar-darwin-arm64
  • cp <binary> $GOPATH/bin/fuelsequencerd to copy the binary to the GOPATH/bin/ directory.
  • chmod +x $GOPATH/bin/fuelsequencerd to make the binary executable.
  • fuelsequencerd version to verify that the binary is working.

Try the binary:

fuelsequencerd version  # expect seq-mainnet-1.2-improved-sidecar

Initialise the node directory, giving your node a meaningful name:

fuelsequencerd init <node-name> --chain-id seq-mainnet-1

Copy the downloaded genesis file to ~/.fuelsequencer/config/genesis.json:

cp <path/to/genesis.json> ~/.fuelsequencer/config/genesis.json

Configure the node (part 1: ~/.fuelsequencer/config/app.toml):

  • Set minimum-gas-prices = "10fuel".
  • Configure [sidecar]:
    • Ensure that enabled = false.

Configure the node (part 2: ~/.fuelsequencer/config/config.toml):

  • Configure [p2p]:
    • Set persistent_peers = "fc5fd264190e4a78612ec589994646268b81f14e@80.64.208.207:26656".
  • Configure [mempool]:
    • Set max_tx_bytes = 1258291 (1.2MiB)
    • Set max_txs_bytes = 23068672 (22MiB)
  • Configure [rpc]:
    • Set max_body_bytes = 1153434 (optional - relevant for public RPC).

Note: Ensuring consistent CometBFT mempool parameters across all network nodes is important to reduce transaction delays. This includes mempool.size, mempool.max_txs_bytes, and mempool.max_tx_bytes in config.toml and minimum-gas-prices in app.toml, as pointed out above.

Install Cosmovisor

To install Cosmovisor, run go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@latest

Set the environment variables:

If you're running on a zsh terminal...
echo "# Setup Cosmovisor" >> ~/.zshrc
echo "export DAEMON_NAME=fuelsequencerd" >> ~/.zshrc
echo "export DAEMON_HOME=$HOME/.fuelsequencer" >> ~/.zshrc
echo "export DAEMON_ALLOW_DOWNLOAD_BINARIES=true" >> ~/.zshrc
echo "export DAEMON_LOG_BUFFER_SIZE=512" >> ~/.zshrc
echo "export DAEMON_RESTART_AFTER_UPGRADE=true" >> ~/.zshrc
echo "export UNSAFE_SKIP_BACKUP=true" >> ~/.zshrc
echo "export DAEMON_SHUTDOWN_GRACE=15s" >> ~/.zshrc

# You can check https://docs.cosmos.network/main/tooling/cosmovisor for more configuration options.

Apply to your current session: source ~/.zshrc

If you're running on a bash terminal...
echo "# Setup Cosmovisor" >> ~/.bashrc
echo "export DAEMON_NAME=fuelsequencerd" >> ~/.bashrc
echo "export DAEMON_HOME=$HOME/.fuelsequencer" >> ~/.bashrc
echo "export DAEMON_ALLOW_DOWNLOAD_BINARIES=true" >> ~/.bashrc
echo "export DAEMON_LOG_BUFFER_SIZE=512" >> ~/.bashrc
echo "export DAEMON_RESTART_AFTER_UPGRADE=true" >> ~/.bashrc
echo "export UNSAFE_SKIP_BACKUP=true" >> ~/.bashrc
echo "export DAEMON_SHUTDOWN_GRACE=15s" >> ~/.bashrc

# You can check https://docs.cosmos.network/main/tooling/cosmovisor for more configuration options.

Apply to your current session: source ~/.bashrc

You can now test that cosmovisor was installed properly:

cosmovisor version

Initialise Cosmovisor directories (hint: whereis fuelsequencerd for the path):

cosmovisor init <path/to/fuelsequencerd>

At this point cosmovisor run will be the equivalent of running fuelsequencerd, however you should not run the node for now.

Configure State Sync

State Sync allows a node to get synced up quickly.

To configure State Sync, you will need to set these values in ~/.fuelsequencer/config/config.toml under [statesync]:

  • enable = true to enable State Sync
  • rpc_servers = ...
  • trust_height = ...
  • trust_hash = ...

The last three values can be obtained from the explorer.

You will need to specify at least two comma-separated RPC servers in rpc_servers. You can either refer to the list of alternate RPC servers above or use the same one twice.

Running the Sequencer

At this point you should already be able to run cosmovisor run start to run the Sequencer. However, it is highly recommended to run the Sequencer as a background service.

Some examples are provided below for Linux and Mac. You will need to replicate the environment variables defined when setting up Cosmovisor.

Linux

On Linux, you can use systemd to run the Sequencer in the background. Knowledge of how to use systemd is assumed here.

Here's an example service file with some placeholder (<...>) values that must be filled-in:

Click me...
[Unit]
Description=Sequencer Node
After=network.target

[Service]
Type=simple
User=<USER>
ExecStart=/home/<USER>/go/bin/cosmovisor run start
Restart=on-failure
RestartSec=3
LimitNOFILE=4096

Environment="DAEMON_NAME=fuelsequencerd"
Environment="DAEMON_HOME=/home/<USER>/.fuelsequencer"
Environment="DAEMON_ALLOW_DOWNLOAD_BINARIES=true"
Environment="DAEMON_LOG_BUFFER_SIZE=512"
Environment="DAEMON_RESTART_AFTER_UPGRADE=true"
Environment="UNSAFE_SKIP_BACKUP=true"
Environment="DAEMON_SHUTDOWN_GRACE=15s"

[Install]
WantedBy=multi-user.target

Mac

On Mac, you can use launchd to run the Sequencer in the background. Knowledge of how to use launchd is assumed here.

Here's an example plist file with some placeholder ([...]) values that must be filled-in:

Click me...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>fuel.sequencer</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/[User]/go/bin/cosmovisor</string>
        <string>run</string>
        <string>start</string>
    </array>

    <key>UserName</key>
    <string>[User]</string>

    <key>EnvironmentVariables</key>
    <dict>
        <key>DAEMON_NAME</key>
        <string>fuelsequencerd</string>
        <key>DAEMON_HOME</key>
        <string>/Users/[User]/.fuelsequencer</string>
        <key>DAEMON_ALLOW_DOWNLOAD_BINARIES</key>
        <string>true</string>
        <key>DAEMON_LOG_BUFFER_SIZE</key>
        <string>512</string>
        <key>DAEMON_RESTART_AFTER_UPGRADE</key>
        <string>true</string>
        <key>UNSAFE_SKIP_BACKUP</key>
        <string>true</string>
        <key>DAEMON_SHUTDOWN_GRACE</key>
        <string>15s</string>
    </dict>

    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>

    <key>HardResourceLimits</key>
    <dict>
        <key>NumberOfFiles</key>
        <integer>4096</integer>
    </dict>

    <key>StandardOutPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sequencer.out</string>
    <key>StandardErrorPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sequencer.err</string>
</dict>
</plist>

References

Based on material from:

  • https://docs.cosmos.network/main/tooling/cosmovisor
  • https://docs.osmosis.zone/overview/validate/joining-mainnet#set-up-cosmovisor

Run Sequencer Validator

Typical Setup

The validator setup will consist of a Fuel Sequencer, Sidecar, and a connection to an Ethereum Mainnet Node.

fuel sequencer validator

Prerequisites

The guide assumes that Golang is installed in order to run Cosmovisor. We recommend installing version 1.21+.

Run an Ethereum Mainnet Full Node

To ensure the highest performance and reliability of the Sequencer infrastructure, running your own Ethereum Mainnet full node is a requirement. Avoiding the use of third-party services for Ethereum node operations significantly helps the Sequencer network's liveness. Please note these recommended node configurations:

--syncmode=snap
--gcmode=full

Configure the Sequencer

Obtain binary and genesis from this repository:

  • Binary from: https://github.com/FuelLabs/fuel-sequencer-deployments/releases/tag/seq-mainnet-1.2-improved-sidecar
    • For example:
      • fuelsequencerd-seq-mainnet-1.2-improved-sidecar-darwin-arm64 for Apple Silicon
      • fuelsequencerd-seq-mainnet-1.2-improved-sidecar-darwin-amd64 for Linux x64
  • Genesis from: https://github.com/FuelLabs/fuel-sequencer-deployments/blob/main/seq-mainnet-1/genesis.json

Download the right binary based on your architecture to $GOPATH/bin/ with the name fuelsequencerd:

  • echo $GOPATH to ensure it exists. If not, go might not be installed.
  • Make sure that your GOPATH is set properly in your .bashrc or .zshrc file. Run source ~/.bashrc or source ~/.zshrc to apply the changes.
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
  • mkdir $GOPATH/bin/ if the directory does not exist.
  • wget <url/to/binary> to download the binary, or any equivalent approach. For example:
wget https://github.com/FuelLabs/fuel-sequencer-deployments/releases/download/seq-mainnet-1.2-improved-sidecar/fuelsequencerd-seq-mainnet-1.2-improved-sidecar-darwin-arm64
  • cp <binary> $GOPATH/bin/fuelsequencerd to copy the binary to the GOPATH/bin/ directory.
  • chmod +x $GOPATH/bin/fuelsequencerd to make the binary executable.
  • fuelsequencerd version to verify that the binary is working.

Try the binary:

fuelsequencerd version  # expect seq-mainnet-1.2-improved-sidecar

Initialise the node directory, giving your node a meaningful name:

fuelsequencerd init <node-name> --chain-id seq-mainnet-1

Copy the downloaded genesis file to ~/.fuelsequencer/config/genesis.json:

cp <path/to/genesis.json> ~/.fuelsequencer/config/genesis.json

Configure the node (part 1: ~/.fuelsequencer/config/app.toml):

  • Set minimum-gas-prices = "10fuel".
  • Configure [sidecar]:
    • Ensure that enabled = true.
    • Ensure that address is where the Sidecar will run.
  • Configure [api]:
    • Set swagger=true (optional).
    • Set rpc-max-body-bytes = 1153434 (optional - relevant for public REST).
  • Configure [commitments]:
    • Set api-enabled = true (optional - relevant for public REST).
  • Configure [state-sync]:
    • Set snapshot-interval = 1000 (optional - to provide state-sync service).
  • Configure:
    • Set rpc-read-timeout = 10 (optional - relevant for public REST).
    • Set rpc-write-timeout = 0 (optional - relevant for public REST).

WARNING: leaving the [commitments] API accessible to anyone can lead to DoS! It is highly recommended to handle whitelisting or authentication by a reverse proxy like Traefik for gRPC if the commitments API is enabled.

Configure the node (part 2: ~/.fuelsequencer/config/config.toml):

  • Configure [p2p]:
    • Set persistent_peers = "fc5fd264190e4a78612ec589994646268b81f14e@80.64.208.207:26656".
  • Configure [mempool]:
    • Set max_tx_bytes = 1258291 (1.2MiB)
    • Set max_txs_bytes = 23068672 (22MiB)
  • Configure [rpc]:
    • Set max_body_bytes = 1153434 (optional - relevant for public RPC).

Note: Ensuring consistent CometBFT mempool parameters across all network nodes is important to reduce transaction delays. This includes mempool.size, mempool.max_txs_bytes, and mempool.max_tx_bytes in config.toml and minimum-gas-prices in app.toml, as pointed out above.

Install Cosmovisor

To install Cosmovisor, run go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@latest

Set the environment variables:

If you're running on a zsh terminal...
echo "# Setup Cosmovisor" >> ~/.zshrc
echo "export DAEMON_NAME=fuelsequencerd" >> ~/.zshrc
echo "export DAEMON_HOME=$HOME/.fuelsequencer" >> ~/.zshrc
echo "export DAEMON_ALLOW_DOWNLOAD_BINARIES=true" >> ~/.zshrc
echo "export DAEMON_LOG_BUFFER_SIZE=512" >> ~/.zshrc
echo "export DAEMON_RESTART_AFTER_UPGRADE=true" >> ~/.zshrc
echo "export UNSAFE_SKIP_BACKUP=true" >> ~/.zshrc
echo "export DAEMON_SHUTDOWN_GRACE=15s" >> ~/.zshrc

# You can check https://docs.cosmos.network/main/tooling/cosmovisor for more configuration options.

Apply to your current session: source ~/.zshrc

If you're running on a bash terminal...
echo "# Setup Cosmovisor" >> ~/.bashrc
echo "export DAEMON_NAME=fuelsequencerd" >> ~/.bashrc
echo "export DAEMON_HOME=$HOME/.fuelsequencer" >> ~/.bashrc
echo "export DAEMON_ALLOW_DOWNLOAD_BINARIES=true" >> ~/.bashrc
echo "export DAEMON_LOG_BUFFER_SIZE=512" >> ~/.bashrc
echo "export DAEMON_RESTART_AFTER_UPGRADE=true" >> ~/.bashrc
echo "export UNSAFE_SKIP_BACKUP=true" >> ~/.bashrc
echo "export DAEMON_SHUTDOWN_GRACE=15s" >> ~/.bashrc

# You can check https://docs.cosmos.network/main/tooling/cosmovisor for more configuration options.

Apply to your current session: source ~/.bashrc

You can now test that cosmovisor was installed properly:

cosmovisor version

Initialise Cosmovisor directories (hint: whereis fuelsequencerd for the path):

cosmovisor init <path/to/fuelsequencerd>

At this point cosmovisor run will be the equivalent of running fuelsequencerd, however you should not run the node for now.

Configure State Sync

State Sync allows a node to get synced up quickly.

To configure State Sync, you will need to set these values in ~/.fuelsequencer/config/config.toml under [statesync]:

  • enable = true to enable State Sync
  • rpc_servers = ...
  • trust_height = ...
  • trust_hash = ...

The last three values can be obtained from the explorer.

You will need to specify at least two comma-separated RPC servers in rpc_servers. You can either refer to the list of alternate RPC servers above or use the same one twice.

Run the Sidecar

At this point you should already be able to run fuelsequencerd start-sidecar with the right flags, to run the Sidecar. However, it is highly recommended to run the Sidecar as a background service.

It is also very important to ensure that you provide all the necessary flags when running the Sidecar to ensure that it can connect to an Ethereum node and to the Sequencer node, and is also accessible by the Sequencer node. The most important flags are:

  • host: host for the gRPC server to listen on
  • port: port for the gRPC server to listen on
  • eth_ws_url: Ethereum node WebSocket endpoint
  • eth_rpc_url: Ethereum node RPC endpoint
  • eth_contract_address: address in hex format of the contract to monitor for logs. This MUST be set to 0xBa0e6bF94580D49B5Aaaa54279198D424B23eCC3.
  • sequencer_grpc_url: Sequencer node gRPC endpoint

Linux

On Linux, you can use systemd to run the Sequencer in the background. Knowledge of how to use systemd is assumed here.

Here's an example service file with some placeholder (<...>) values that must be filled-in:

Click me...
[Unit]
Description=Sidecar
After=network.target

[Service]
Type=simple
User=<USER>
ExecStart=<HOME>/go/bin/fuelsequencerd start-sidecar \
    --host "0.0.0.0" \
    --sequencer_grpc_url "127.0.0.1:9090" \
    --eth_ws_url "<ETHEREUM_NODE_WS>" \
    --eth_rpc_url "<ETHEREUM_NODE_RPC>" \
    --eth_contract_address "0xBa0e6bF94580D49B5Aaaa54279198D424B23eCC3"
Restart=on-failure
RestartSec=3
LimitNOFILE=4096

[Install]
WantedBy=multi-user.target

Mac

On Mac, you can use launchd to run the Sequencer in the background. Knowledge of how to use launchd is assumed here.

Here's an example plist file with some placeholder ([...]) values that must be filled-in:

Click me...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>fuel.sidecar</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/[User]/go/bin/fuelsequencerd</string>
        <string>start-sidecar</string>
        <string>--host</string>
        <string>0.0.0.0</string>
        <string>--sequencer_grpc_url</string>
        <string>127.0.0.1:9090</string>
        <string>--eth_ws_url</string>
        <string>[ETHEREUM_NODE_WS]</string>
        <string>--eth_rpc_url</string>
        <string>[ETHEREUM_NODE_RPC]</string>
        <string>--eth_contract_address</string>
        <string>0xBa0e6bF94580D49B5Aaaa54279198D424B23eCC3</string>
    </array>

    <key>UserName</key>
    <string>[User]</string>

    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>

    <key>HardResourceLimits</key>
    <dict>
        <key>NumberOfFiles</key>
        <integer>4096</integer>
    </dict>

    <key>StandardOutPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sidecar.out</string>
    <key>StandardErrorPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sidecar.err</string>
</dict>
</plist>

Run the Sequencer

At this point you should already be able to run cosmovisor run start to run the Sequencer. However, it is highly recommended to run the Sequencer as a background service.

Some examples are provided below for Linux and Mac. You will need to replicate the environment variables defined when setting up Cosmovisor.

Linux

On Linux, you can use systemd to run the Sequencer in the background. Knowledge of how to use systemd is assumed here.

Here's an example service file with some placeholder (<...>) values that must be filled-in:

Click me...
[Unit]
Description=Sequencer Node
After=network.target

[Service]
Type=simple
User=<USER>
ExecStart=/home/<USER>/go/bin/cosmovisor run start
Restart=on-failure
RestartSec=3
LimitNOFILE=4096

Environment="DAEMON_NAME=fuelsequencerd"
Environment="DAEMON_HOME=/home/<USER>/.fuelsequencer"
Environment="DAEMON_ALLOW_DOWNLOAD_BINARIES=true"
Environment="DAEMON_LOG_BUFFER_SIZE=512"
Environment="DAEMON_RESTART_AFTER_UPGRADE=true"
Environment="UNSAFE_SKIP_BACKUP=true"
Environment="DAEMON_SHUTDOWN_GRACE=15s"

[Install]
WantedBy=multi-user.target

Mac

On Mac, you can use launchd to run the Sequencer in the background. Knowledge of how to use launchd is assumed here.

Here's an example plist file with some placeholder ([...]) values that must be filled-in:

Click me...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>fuel.sequencer</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/[User]/go/bin/cosmovisor</string>
        <string>run</string>
        <string>start</string>
    </array>

    <key>UserName</key>
    <string>[User]</string>

    <key>EnvironmentVariables</key>
    <dict>
        <key>DAEMON_NAME</key>
        <string>fuelsequencerd</string>
        <key>DAEMON_HOME</key>
        <string>/Users/[User]/.fuelsequencer</string>
        <key>DAEMON_ALLOW_DOWNLOAD_BINARIES</key>
        <string>true</string>
        <key>DAEMON_LOG_BUFFER_SIZE</key>
        <string>512</string>
        <key>DAEMON_RESTART_AFTER_UPGRADE</key>
        <string>true</string>
        <key>UNSAFE_SKIP_BACKUP</key>
        <string>true</string>
        <key>DAEMON_SHUTDOWN_GRACE</key>
        <string>15s</string>
    </dict>

    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>

    <key>HardResourceLimits</key>
    <dict>
        <key>NumberOfFiles</key>
        <integer>4096</integer>
    </dict>

    <key>StandardOutPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sequencer.out</string>
    <key>StandardErrorPath</key>
    <string>/Users/[User]/Library/Logs/fuel-sequencer.err</string>
</dict>
</plist>

Creating an Account

To run a validator, you will need to have a Sequencer account address. Generate an address with a key name:

fuelsequencerd keys add <NAME> # for a brand new key

# or

fuelsequencerd keys add <NAME> --recover # to create from a mnemonic

This will give you an output with an address (e.g. fuelsequencer1l7qk9umswg65av0zygyymgx5yg0fx4g0dpp2tl) and a private mnemonic, if you generated a brand new key. Store the mnemonic safely.

Fuel Sequencer addresses also have an Ethereum-compatible (i.e. hex) format. To generate the hex address corresponding to your Sequencer address, run the following:

fuelsequencerd keys parse <ADDRESS>

This will give an output in this form:

bytes: FF8162F37072354EB1E222084DA0D4221E93550F
human: fuelsequencer

Adding the 0x prefix to the address in the first line gives you your Ethereum-compatible address, used to deposit into and interact with your Sequencer address from Ethereum. In this case, it's 0xFF8162F37072354EB1E222084DA0D4221E93550F.

Funding the Account

Ensure your mainnet Ethereum account (EOA) has sufficient ETH to cover gas fees and FUEL tokens to transfer to your Sequencer account.

Important Addresses

Token Approval

Before proceeding, you must approve the Fuel token contract to allow the transfer of tokens.

In the Fuel Token Etherscan contract UI, use the approve (0x095ea7b3) function:

  • Spender (address): Set this to the Sequencer Interface (Bridge) address: 0xca0c6B264f0F9958Ec186eb2EAa208966187D866.

  • Value (uint256): Enter the number of tokens to approve, including 9 additional decimal places. For unlimited approval, use:

    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    

Mainnet Etherscan Approval UI

Bridging Tokens

To bridge tokens, connect your Ethereum wallet by clicking "Connect to Web3" in the top left. Then, use the depositFor (0x36efd6f) function to fund your sequencer account.

Transfer your FUEL tokens using the Sequencer Interface (Bridge)Etherscan UI.

Mainnet Etherscan UI

  • Amount (uint256): Enter the number of tokens to send, including 9 additional decimal places.
  • Recipient address: Enter the Ethereum-compatible address you generated earlier (e.g., 0xFF8162F37072354EB1E222084DA0D4221E93550F).

Click "Write" to confirm the transaction. The transfer may take ~20 minutes to process.

Verifying Funds

To verify your funds, enter your sequencer account address (i.e. fuelsequencer1l7qk9umswg65av0zygyymgx5yg0fx4g0dpp2tl) in the mainnet block explorer.

Mainnet Block Explorer

⚠ WARNING: Always test with a small transfer before bridging FUEL tokens.

Withdrawals

Withdrawals can be easily initiated through the CLI and will be settled on Ethereum approximately 3 days later, as the commitment and bridge finalizations must be completed first.

Identify the account from which you wish to withdraw. Use the following command to list all previously created account names matching your account address above:

fuelsequencerd keys list

Example output:

address: fuelsequencer1zzu4804kp6m6whzza6r75g7mnme2ahqkjuw4kf
  name: my-mainnet-validator
  pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Al6W+Ttrscm/8njeMOt79T0BOdphfWGXrDLij+O3g19N"}'
  type: local

Verify that this is the correct address and account name from which you wish to withdraw.

To initiate the withdrawal, use the following command where <eth-destination-address> is any Ethereum address you wish to withdraw to and <amount-in-fuel> is the amount of FUEL you wish to withdraw:

Note: The amount in FUEL must include 9 decimal places.

fuelsequencerd tx bridge withdraw-to-ethereum <eth-destination-address> <amount-in-fuel> \
  --from=<key> \
  --gas-prices=10fuel \
  --gas=auto \
  --gas-adjustment 1.5 \
  --node="https://fuel-rpc.polkachu.com/" \
  --chain-id="seq-mainnet-1"

For example:

fuelsequencerd tx bridge withdraw-to-ethereum 0xd70080dE4535db4A64798a23619Db64fB28fD079 1fuel \
    --from=my-mainnet-validator \
    --gas-prices=10fuel \
    --gas=auto \
    --gas-adjustment 1.5 \
    --node="https://fuel-rpc.polkachu.com/" \
    --chain-id="seq-mainnet-1"

Review the transaction details and confirm the transaction by typing yes when prompted:

gas estimate: 106942
auth_info:
  fee:
    amount:
    - amount: "1069420"
      denom: fuel
    gas_limit: "106942"
    granter: ""
    payer: ""
  signer_infos: []
  tip: null
body:
  extension_options: []
  memo: ""
  messages:
  - '@type': /fuelsequencer.bridge.v1.MsgWithdrawToEthereum
    amount:
      amount: "1"
      denom: fuel
    from: fuelsequencer1zzu4804kp6m6whzza6r75g7mnme2ahqkjuw4kf
    to: 0xd70080dE4535db4A64798a23619Db64fB28fD079
  non_critical_extension_options: []
  timeout_height: "0"
signatures: []
confirm transaction before signing and broadcasting [y/N]:

If the transaction is successful, you will receive a transaction hash, which you can paste and monitor the status of your withdrawal here:

code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: ""
timestamp: ""
tx: null
txhash: AD541CE1DCDBD8638C5DFD3C7AF3A3AAF8B9CD0AF265C3AFD96633CE8FAF4CF4

Block Explorer Withdrawal

After verifying your withdrawal on the shared sequencer explorer, visit Simply Staking and connect your wallet. Navigate to the Withdrawal tab on the right to monitor the progress of your withdrawal.

Simply Staking Withdrawal

Once the 3-day waiting period has passed, the withdrawal will require manual action to pull the funds out.

Create the Validator

To create the validator, a prerequisite is to have at least 1FUEL, with enough extra to pay for gas fees. You can check your balance from the explorer.

Once you have FUEL tokens, run the following to create a validator, using the name of the account that you created in the previous steps:

fuelsequencerd tx staking create-validator path/to/validator.json \
    --from <NAME> \
    --gas auto \
    --gas-prices 10fuel \
    --gas-adjustment 1.5 \
    --chain-id seq-mainnet-1

...where validator.json contains:

{
 "pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"<PUBKEY>"},
 "amount": "1000000000fuel",
 "moniker": "<MONIKER>",
 "identity": "<OPTIONAL-IDENTITY>",
 "website": "<OPTIONAL-WEBSITE>",
 "security": "<OPTIONAL-EMAIL>",
 "details": "<OPTIONAL-DETAILS>",
 "commission-rate": "0.05",
 "commission-max-rate": "<MAX-RATE>",
 "commission-max-change-rate": "<MAX-CHANGE-RATE>",
 "min-self-delegation": "1"
}

...where the pubkey can be obtained using fuelsequencerd tendermint show-validator.

What to Expect

  • The Sequencer should show block syncing.
  • The Sidecar should show block extraction. Occasionally it also receives requests for events.

Tendermint KMS

If you will be using tmkms, make sure that in the config:

  • Chain ID is set to seq-mainnet-1 wherever applicable
  • account_key_prefix = "fuelsequencerpub"
  • consensus_key_prefix = "fuelsequencervalconspub"
  • sign_extensions = true
  • protocol_version = "v0.34"

Additional Advanced Configuration

Sidecar flags:

  • development: starts the sidecar in development mode.
  • eth_max_block_range: max number of Ethereum blocks queried at one go.
  • eth_min_logs_query_interval: minimum wait between successive queries for logs.
  • unsafe_eth_start_block: the Ethereum block to start querying from.
  • unsafe_eth_end_block: the last Ethereum block to query. Incorrect use can cause the validator to propose empty blocks, leading to slashing!
  • sequencer_path_to_cert_file: path to the certificate file of the Sequencer infrastructure for secure communication. Specify this value if the Sequencer infrastructure was set up using TLS.
  • sidecar_path_to_cert_file: path to the certificate file of the sidecar server for secure communication. Specify this value if you want to set up a sidecar server with TLS.
  • sidecar_path_to_key_file: path to the private key file of the sidecar server for secure communication. Specify this value if you want to set up a sidecar server with TLS.
  • prometheus_enabled: enables serving of prometheus metrics.
  • prometheus_listen_address: address to listen for prometheus collectors (default ":8081").
  • prometheus_max_open_connections: max number of simultaneous connections (default 3).
  • prometheus_namespace: instrumentation namespace (default "sidecar").
  • prometheus_read_header_timeout: amount of time allowed to read request headers (default 10s).
  • prometheus_write_timeout: maximum duration before timing out writes of the response (default 10s).

Sidecar client flags:

  • sidecar_grpc_url: the sidecar's gRPC endpoint.
  • query_timeout: how long to wait before the request times out.

References

Based on material from:

  • https://docs.cosmos.network/main/tooling/cosmovisor
  • https://docs.osmosis.zone/overview/validate/joining-mainnet#set-up-cosmovisor

Getting Started

Installation Guide

Please visit the Fuel installation guide to install the Fuel toolchain binaries and prerequisites.

forc is Sway equivalent of Rust's cargo. fuel-core is a Fuel full node implementation.

There are two main ways you can use the Fuel Rust SDK:

  1. Creating a new Sway project with forc and running the tests
  2. Creating a standalone project and importing the fuels-rs crate

Creating a new project with Forc

You can create a new Sway project with

forc new <Project name>

Or you can initialize a project within an existing folder with

forc init

Adding a Rust integration test to the Sway project

Now that we have a new project, we can add a Rust integration test using a cargo generate template. If cargo generate is not already installed, you can install it with:

cargo install cargo-generate

Note You can learn more about cargo generate by visiting its repository.

Let's generate the default test harness with the following command:

cargo generate --init fuellabs/sway templates/sway-test-rs --name <Project name> --force

--force forces your --name input to retain your desired casing for the {{project-name}} placeholder in the template. Otherwise, cargo-generate automatically converts it to kebab-case. With --force, this means that both my_fuel_project and my-fuel-project are valid project names, depending on your needs.

Before running test, we need to build the Sway project with:

forc build

Afterwards, we can run the test with:

cargo test

Note If you need to capture output from the tests, use one of the following commands:

cargo test -- --nocapture

Importing the Fuel Rust SDK

Add these dependencies on your Cargo.toml:

fuels = "0.66.0"

Note We're using version 0.66.0 of the SDK, which is the latest version at the time of this writing.

And then, in your Rust file that's going to make use of the SDK:

use fuels::prelude::*;

The Fuel Rust SDK source code

Another way to experience the SDK is to look at the source code. The e2e/tests/ folder is full of integration tests that go through almost all aspects of the SDK.

Note Before running the tests, we need to build all the Sway test projects. The file packages/fuels/Forc.toml contains a `[workspace], which members are the paths to all integration tests. To build these tests, run the following command:

forc build --release --path e2e

forc can also be used to clean and format the test projects. Check the help output for more info.

After building the projects, we can run the tests with

cargo test

If you need all targets and all features, you can run

cargo test --all-targets --all-features

Note If you need to capture output from the tests, you can run

cargo test -- --nocapture

More in-depth Fuel and Sway knowledge

Read The Sway Book for more in-depth knowledge about Sway, the official smart contract language for the Fuel Virtual Machine.

Connecting to a Fuel node

At a high level, you can use the Fuel Rust SDK to build Rust-based applications that can run computations on the Fuel Virtual Machine through interactions with smart contracts written in Sway.

For this interaction to work, the SDK must be able to communicate with a fuel-core node; you have two options at your disposal:

  1. Use the testnet or run a Fuel node (using fuel-core) and instantiate a provider that points to that node's IP and port.
  2. Use the SDK's native launch_provider_and_get_wallet() that runs a short-lived test Fuel node;

The second option is ideal for smart contract testing, as you can quickly spin up and tear down nodes between specific test cases.

For application building, you should use the first option.

Connecting to the Testnet or an external node

We can interact with the Testnet node by using the following example.

```rust\n#[cfg(test)]
mod tests {
    use std::time::Duration;

    use fuels::prelude::Result;

    #[ignore = "testnet currently not compatible with the sdk"]
    #[tokio::test]
    async fn connect_to_fuel_node() -> Result<()> {
        // ANCHOR: connect_to_testnet
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Create a provider pointing to the testnet.
        let provider = Provider::connect("testnet.fuel.network").await.unwrap();

        // Setup a private key
        let secret = SecretKey::from_str(
            "a1447cd75accc6b71a976fd3401a1f6ce318d27ba660b0315ee6ac347bf39568",
        )?;

        // Create the wallet
        let wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));

        // Get the wallet address. Used later with the faucet
        dbg!(wallet.address().to_string());
        // ANCHOR_END: connect_to_testnet

        let provider = setup_test_provider(vec![], vec![], None, None).await?;
        let port = provider.url().split(':').last().unwrap();

        // ANCHOR: local_node_address
        let _provider = Provider::connect(format!("127.0.0.1:{port}")).await?;
        // ANCHOR_END: local_node_address

        Ok(())
    }

    #[tokio::test]
    async fn query_the_blockchain() -> Result<()> {
        // ANCHOR: setup_test_blockchain
        use fuels::prelude::*;

        // Set up our test blockchain.

        // Create a random wallet (more on wallets later).
        // ANCHOR: setup_single_asset
        let wallet = WalletUnlocked::new_random(None);

        // How many coins in our wallet.
        let number_of_coins = 1;

        // The amount/value in each coin in our wallet.
        let amount_per_coin = 3;

        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            number_of_coins,
            amount_per_coin,
        );
        // ANCHOR_END: setup_single_asset

        // ANCHOR: configure_retry
        let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?;
        let provider = setup_test_provider(coins.clone(), vec![], None, None)
            .await?
            .with_retry_config(retry_config);
        // ANCHOR_END: configure_retry
        // ANCHOR_END: setup_test_blockchain

        // ANCHOR: get_coins
        let consensus_parameters = provider.consensus_parameters().await?;
        let coins = provider
            .get_coins(wallet.address(), *consensus_parameters.base_asset_id())
            .await?;
        assert_eq!(coins.len(), 1);
        // ANCHOR_END: get_coins

        // ANCHOR: get_spendable_resources
        let filter = ResourceFilter {
            from: wallet.address().clone(),
            amount: 1,
            ..Default::default()
        };
        let spendable_resources = provider.get_spendable_resources(filter).await?;
        assert_eq!(spendable_resources.len(), 1);
        // ANCHOR_END: get_spendable_resources

        // ANCHOR: get_balances
        let _balances = provider.get_balances(wallet.address()).await?;
        // ANCHOR_END: get_balances

        Ok(())
    }
}\n```

For detailed information about various testnet networks and their optimal toolchain configurations for your project, please visit the following link:

networks

In the code example, we connected a new provider to the Testnet node and created a new wallet from a private key.

Note: New wallets on the Testnet will not have any assets! They can be obtained by providing the wallet address to the faucet at

faucet-testnet.fuel.network

Once the assets have been transferred to the wallet, you can reuse it in other tests by providing the private key!

In addition to the faucet, there is a block explorer for the Testnet at

block-explorer

If you want to connect to another node just change the URL or IP and port. For example, to connect to a local node that was created with fuel-core you can use:

```rust\n#[cfg(test)]
mod tests {
    use std::time::Duration;

    use fuels::prelude::Result;

    #[ignore = "testnet currently not compatible with the sdk"]
    #[tokio::test]
    async fn connect_to_fuel_node() -> Result<()> {
        // ANCHOR: connect_to_testnet
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Create a provider pointing to the testnet.
        let provider = Provider::connect("testnet.fuel.network").await.unwrap();

        // Setup a private key
        let secret = SecretKey::from_str(
            "a1447cd75accc6b71a976fd3401a1f6ce318d27ba660b0315ee6ac347bf39568",
        )?;

        // Create the wallet
        let wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));

        // Get the wallet address. Used later with the faucet
        dbg!(wallet.address().to_string());
        // ANCHOR_END: connect_to_testnet

        let provider = setup_test_provider(vec![], vec![], None, None).await?;
        let port = provider.url().split(':').last().unwrap();

        // ANCHOR: local_node_address
        let _provider = Provider::connect(format!("127.0.0.1:{port}")).await?;
        // ANCHOR_END: local_node_address

        Ok(())
    }

    #[tokio::test]
    async fn query_the_blockchain() -> Result<()> {
        // ANCHOR: setup_test_blockchain
        use fuels::prelude::*;

        // Set up our test blockchain.

        // Create a random wallet (more on wallets later).
        // ANCHOR: setup_single_asset
        let wallet = WalletUnlocked::new_random(None);

        // How many coins in our wallet.
        let number_of_coins = 1;

        // The amount/value in each coin in our wallet.
        let amount_per_coin = 3;

        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            number_of_coins,
            amount_per_coin,
        );
        // ANCHOR_END: setup_single_asset

        // ANCHOR: configure_retry
        let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?;
        let provider = setup_test_provider(coins.clone(), vec![], None, None)
            .await?
            .with_retry_config(retry_config);
        // ANCHOR_END: configure_retry
        // ANCHOR_END: setup_test_blockchain

        // ANCHOR: get_coins
        let consensus_parameters = provider.consensus_parameters().await?;
        let coins = provider
            .get_coins(wallet.address(), *consensus_parameters.base_asset_id())
            .await?;
        assert_eq!(coins.len(), 1);
        // ANCHOR_END: get_coins

        // ANCHOR: get_spendable_resources
        let filter = ResourceFilter {
            from: wallet.address().clone(),
            amount: 1,
            ..Default::default()
        };
        let spendable_resources = provider.get_spendable_resources(filter).await?;
        assert_eq!(spendable_resources.len(), 1);
        // ANCHOR_END: get_spendable_resources

        // ANCHOR: get_balances
        let _balances = provider.get_balances(wallet.address()).await?;
        // ANCHOR_END: get_balances

        Ok(())
    }
}\n```

Running a short-lived Fuel node with the SDK

You can use the SDK to spin up a local, ideally short-lived Fuel node. Then, you can instantiate a Fuel client, pointing to this node.

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

This approach is ideal for contract testing.

You can also use the test helper setup_test_provider() for this:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

You can also use launch_provider_and_get_wallet(), which abstracts away the setup_test_provider() and the wallet creation, all in one single method:

let wallet = launch_provider_and_get_wallet().await?;

Features

Fuel-core lib

The fuel-core-lib feature allows us to run a fuel-core node without installing the fuel-core binary on the local machine. Using the fuel-core-lib feature flag entails downloading all the dependencies needed to run the fuel-core node.

fuels = { version = "0.70.1", features = ["fuel-core-lib"] }

RocksDB

The rocksdb is an additional feature that, when combined with fuel-core-lib, provides persistent storage capabilities while using fuel-core as a library.

fuels = { version = "0.70.1", features = ["rocksdb"] }

RocksDB

RocksDB enables the preservation of the blockchain's state locally, facilitating its future utilization.

To create or use a local database, follow these instructions:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Note: If the specified database does not exist, a new database will be created at that path. To utilize the code snippets above, either the fuel-core binary must be present, or both the fuel-core-lib and rocksdb features need to be enabled.

Querying the blockchain

Once you set up a provider, you can interact with the Fuel blockchain. Here are a few examples of what you can do with a provider; for a more in-depth overview of the API, check the official provider API documentation.

Set up

You might need to set up a test blockchain first. You can skip this step if you're connecting to an external blockchain.

```rust\n#[cfg(test)]
mod tests {
    use std::time::Duration;

    use fuels::prelude::Result;

    #[ignore = "testnet currently not compatible with the sdk"]
    #[tokio::test]
    async fn connect_to_fuel_node() -> Result<()> {
        // ANCHOR: connect_to_testnet
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Create a provider pointing to the testnet.
        let provider = Provider::connect("testnet.fuel.network").await.unwrap();

        // Setup a private key
        let secret = SecretKey::from_str(
            "a1447cd75accc6b71a976fd3401a1f6ce318d27ba660b0315ee6ac347bf39568",
        )?;

        // Create the wallet
        let wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));

        // Get the wallet address. Used later with the faucet
        dbg!(wallet.address().to_string());
        // ANCHOR_END: connect_to_testnet

        let provider = setup_test_provider(vec![], vec![], None, None).await?;
        let port = provider.url().split(':').last().unwrap();

        // ANCHOR: local_node_address
        let _provider = Provider::connect(format!("127.0.0.1:{port}")).await?;
        // ANCHOR_END: local_node_address

        Ok(())
    }

    #[tokio::test]
    async fn query_the_blockchain() -> Result<()> {
        // ANCHOR: setup_test_blockchain
        use fuels::prelude::*;

        // Set up our test blockchain.

        // Create a random wallet (more on wallets later).
        // ANCHOR: setup_single_asset
        let wallet = WalletUnlocked::new_random(None);

        // How many coins in our wallet.
        let number_of_coins = 1;

        // The amount/value in each coin in our wallet.
        let amount_per_coin = 3;

        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            number_of_coins,
            amount_per_coin,
        );
        // ANCHOR_END: setup_single_asset

        // ANCHOR: configure_retry
        let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?;
        let provider = setup_test_provider(coins.clone(), vec![], None, None)
            .await?
            .with_retry_config(retry_config);
        // ANCHOR_END: configure_retry
        // ANCHOR_END: setup_test_blockchain

        // ANCHOR: get_coins
        let consensus_parameters = provider.consensus_parameters().await?;
        let coins = provider
            .get_coins(wallet.address(), *consensus_parameters.base_asset_id())
            .await?;
        assert_eq!(coins.len(), 1);
        // ANCHOR_END: get_coins

        // ANCHOR: get_spendable_resources
        let filter = ResourceFilter {
            from: wallet.address().clone(),
            amount: 1,
            ..Default::default()
        };
        let spendable_resources = provider.get_spendable_resources(filter).await?;
        assert_eq!(spendable_resources.len(), 1);
        // ANCHOR_END: get_spendable_resources

        // ANCHOR: get_balances
        let _balances = provider.get_balances(wallet.address()).await?;
        // ANCHOR_END: get_balances

        Ok(())
    }
}\n```

Get all coins from an address

This method returns all unspent coins (of a given asset ID) from a wallet.

```rust\n#[cfg(test)]
mod tests {
    use std::time::Duration;

    use fuels::prelude::Result;

    #[ignore = "testnet currently not compatible with the sdk"]
    #[tokio::test]
    async fn connect_to_fuel_node() -> Result<()> {
        // ANCHOR: connect_to_testnet
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Create a provider pointing to the testnet.
        let provider = Provider::connect("testnet.fuel.network").await.unwrap();

        // Setup a private key
        let secret = SecretKey::from_str(
            "a1447cd75accc6b71a976fd3401a1f6ce318d27ba660b0315ee6ac347bf39568",
        )?;

        // Create the wallet
        let wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));

        // Get the wallet address. Used later with the faucet
        dbg!(wallet.address().to_string());
        // ANCHOR_END: connect_to_testnet

        let provider = setup_test_provider(vec![], vec![], None, None).await?;
        let port = provider.url().split(':').last().unwrap();

        // ANCHOR: local_node_address
        let _provider = Provider::connect(format!("127.0.0.1:{port}")).await?;
        // ANCHOR_END: local_node_address

        Ok(())
    }

    #[tokio::test]
    async fn query_the_blockchain() -> Result<()> {
        // ANCHOR: setup_test_blockchain
        use fuels::prelude::*;

        // Set up our test blockchain.

        // Create a random wallet (more on wallets later).
        // ANCHOR: setup_single_asset
        let wallet = WalletUnlocked::new_random(None);

        // How many coins in our wallet.
        let number_of_coins = 1;

        // The amount/value in each coin in our wallet.
        let amount_per_coin = 3;

        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            number_of_coins,
            amount_per_coin,
        );
        // ANCHOR_END: setup_single_asset

        // ANCHOR: configure_retry
        let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?;
        let provider = setup_test_provider(coins.clone(), vec![], None, None)
            .await?
            .with_retry_config(retry_config);
        // ANCHOR_END: configure_retry
        // ANCHOR_END: setup_test_blockchain

        // ANCHOR: get_coins
        let consensus_parameters = provider.consensus_parameters().await?;
        let coins = provider
            .get_coins(wallet.address(), *consensus_parameters.base_asset_id())
            .await?;
        assert_eq!(coins.len(), 1);
        // ANCHOR_END: get_coins

        // ANCHOR: get_spendable_resources
        let filter = ResourceFilter {
            from: wallet.address().clone(),
            amount: 1,
            ..Default::default()
        };
        let spendable_resources = provider.get_spendable_resources(filter).await?;
        assert_eq!(spendable_resources.len(), 1);
        // ANCHOR_END: get_spendable_resources

        // ANCHOR: get_balances
        let _balances = provider.get_balances(wallet.address()).await?;
        // ANCHOR_END: get_balances

        Ok(())
    }
}\n```

Get spendable resources owned by an address

The following example shows how to fetch resources owned by an address. First, you create a ResourceFilter which specifies the target address, asset ID, and amount. You can also define UTXO IDs and message IDs that should be excluded when retrieving the resources:

```rust\n#[cfg(feature = "coin-cache")]
use std::sync::Arc;
use std::{collections::HashMap, fmt::Debug, net::SocketAddr};

mod cache;
mod retry_util;
mod retryable_client;
mod supported_fuel_core_version;
mod supported_versions;

use crate::provider::cache::CacheableRpcs;
pub use cache::TtlConfig;
use cache::{CachedClient, SystemClock};
use chrono::{DateTime, Utc};
use fuel_core_client::client::{
    pagination::{PageDirection, PaginatedResult, PaginationRequest},
    types::{
        balance::Balance,
        contract::ContractBalance,
        gas_price::{EstimateGasPrice, LatestGasPrice},
    },
};
use fuel_core_types::services::executor::TransactionExecutionResult;
use fuel_tx::{
    AssetId, ConsensusParameters, Receipt, Transaction as FuelTransaction, TxId, UtxoId,
};
use fuel_types::{Address, BlockHeight, Bytes32, Nonce};
#[cfg(feature = "coin-cache")]
use fuels_core::types::coin_type_id::CoinTypeId;
use fuels_core::{
    constants::{DEFAULT_GAS_ESTIMATION_BLOCK_HORIZON, DEFAULT_GAS_ESTIMATION_TOLERANCE},
    types::{
        bech32::{Bech32Address, Bech32ContractId},
        block::{Block, Header},
        chain_info::ChainInfo,
        coin::Coin,
        coin_type::CoinType,
        errors::Result,
        message::Message,
        message_proof::MessageProof,
        node_info::NodeInfo,
        transaction::{Transaction, Transactions},
        transaction_builders::{Blob, BlobId},
        transaction_response::TransactionResponse,
        tx_status::TxStatus,
        DryRun, DryRunner,
    },
};
pub use retry_util::{Backoff, RetryConfig};
pub use supported_fuel_core_version::SUPPORTED_FUEL_CORE_VERSION;
use tai64::Tai64;
#[cfg(feature = "coin-cache")]
use tokio::sync::Mutex;

#[cfg(feature = "coin-cache")]
use crate::coin_cache::CoinsCache;
use crate::provider::retryable_client::RetryableClient;

const NUM_RESULTS_PER_REQUEST: i32 = 100;

#[derive(Debug, Clone, PartialEq)]
// ANCHOR: transaction_cost
pub struct TransactionCost {
    pub gas_price: u64,
    pub gas_used: u64,
    pub metered_bytes_size: u64,
    pub total_fee: u64,
}
// ANCHOR_END: transaction_cost

pub(crate) struct ResourceQueries {
    utxos: Vec<UtxoId>,
    messages: Vec<Nonce>,
    asset_id: Option<AssetId>,
    amount: u64,
}

impl ResourceQueries {
    pub fn exclusion_query(&self) -> Option<(Vec<UtxoId>, Vec<Nonce>)> {
        if self.utxos.is_empty() && self.messages.is_empty() {
            return None;
        }

        Some((self.utxos.clone(), self.messages.clone()))
    }

    pub fn spend_query(&self, base_asset_id: AssetId) -> Vec<(AssetId, u64, Option<u32>)> {
        vec![(self.asset_id.unwrap_or(base_asset_id), self.amount, None)]
    }
}

#[derive(Default)]
// ANCHOR: resource_filter
pub struct ResourceFilter {
    pub from: Bech32Address,
    pub asset_id: Option<AssetId>,
    pub amount: u64,
    pub excluded_utxos: Vec<UtxoId>,
    pub excluded_message_nonces: Vec<Nonce>,
}
// ANCHOR_END: resource_filter

impl ResourceFilter {
    pub fn owner(&self) -> Address {
        (&self.from).into()
    }

    pub(crate) fn resource_queries(&self) -> ResourceQueries {
        ResourceQueries {
            utxos: self.excluded_utxos.clone(),
            messages: self.excluded_message_nonces.clone(),
            asset_id: self.asset_id,
            amount: self.amount,
        }
    }
}

/// Encapsulates common client operations in the SDK.
/// Note that you may also use `client`, which is an instance
/// of `FuelClient`, directly, which provides a broader API.
#[derive(Debug, Clone)]
pub struct Provider {
    cached_client: CachedClient<RetryableClient>,
    #[cfg(feature = "coin-cache")]
    coins_cache: Arc<Mutex<CoinsCache>>,
}

impl Provider {
    pub async fn from(addr: impl Into<SocketAddr>) -> Result<Self> {
        let addr = addr.into();
        Self::connect(format!("http://{addr}")).await
    }

    pub fn set_cache_ttl(&mut self, ttl: TtlConfig) {
        self.cached_client.set_ttl(ttl);
    }

    pub async fn clear_cache(&self) {
        self.cached_client.clear().await;
    }

    pub async fn healthy(&self) -> Result<bool> {
        Ok(self.uncached_client().health().await?)
    }

    /// Connects to an existing node at the given address.
    pub async fn connect(url: impl AsRef<str>) -> Result<Provider> {
        let client = CachedClient::new(
            RetryableClient::connect(&url, Default::default()).await?,
            TtlConfig::default(),
            SystemClock,
        );

        Ok(Self {
            cached_client: client,
            #[cfg(feature = "coin-cache")]
            coins_cache: Default::default(),
        })
    }

    pub fn url(&self) -> &str {
        self.uncached_client().url()
    }

    pub async fn blob(&self, blob_id: BlobId) -> Result<Option<Blob>> {
        Ok(self
            .uncached_client()
            .blob(blob_id.into())
            .await?
            .map(|blob| Blob::new(blob.bytecode)))
    }

    pub async fn blob_exists(&self, blob_id: BlobId) -> Result<bool> {
        Ok(self.uncached_client().blob_exists(blob_id.into()).await?)
    }

    /// Sends a transaction to the underlying Provider's client.
    pub async fn send_transaction_and_await_commit<T: Transaction>(
        &self,
        tx: T,
    ) -> Result<TxStatus> {
        #[cfg(feature = "coin-cache")]
        let base_asset_id = *self.consensus_parameters().await?.base_asset_id();

        #[cfg(feature = "coin-cache")]
        self.check_inputs_already_in_cache(&tx.used_coins(&base_asset_id))
            .await?;

        let tx = self.prepare_transaction_for_sending(tx).await?;
        let tx_status = self
            .uncached_client()
            .submit_and_await_commit(&tx.clone().into())
            .await?
            .into();

        #[cfg(feature = "coin-cache")]
        if matches!(
            tx_status,
            TxStatus::SqueezedOut { .. } | TxStatus::Revert { .. }
        ) {
            self.coins_cache
                .lock()
                .await
                .remove_items(tx.used_coins(&base_asset_id))
        }

        Ok(tx_status)
    }

    async fn prepare_transaction_for_sending<T: Transaction>(&self, mut tx: T) -> Result<T> {
        let consensus_parameters = self.consensus_parameters().await?;
        tx.precompute(&consensus_parameters.chain_id())?;

        let chain_info = self.chain_info().await?;
        let Header {
            height: latest_block_height,
            state_transition_bytecode_version: latest_chain_executor_version,
            ..
        } = chain_info.latest_block.header;

        if tx.is_using_predicates() {
            tx.estimate_predicates(self, Some(latest_chain_executor_version))
                .await?;
            tx.clone()
                .validate_predicates(&consensus_parameters, latest_block_height)?;
        }

        self.validate_transaction(tx.clone()).await?;

        Ok(tx)
    }

    pub async fn send_transaction<T: Transaction>(&self, tx: T) -> Result<TxId> {
        let tx = self.prepare_transaction_for_sending(tx).await?;
        self.submit(tx).await
    }

    pub async fn await_transaction_commit<T: Transaction>(&self, id: TxId) -> Result<TxStatus> {
        Ok(self
            .uncached_client()
            .await_transaction_commit(&id)
            .await?
            .into())
    }

    async fn validate_transaction<T: Transaction>(&self, tx: T) -> Result<()> {
        let tolerance = 0.0;
        let TransactionCost { gas_used, .. } = self
            .estimate_transaction_cost(tx.clone(), Some(tolerance), None)
            .await?;

        tx.validate_gas(gas_used)?;

        Ok(())
    }

    #[cfg(not(feature = "coin-cache"))]
    async fn submit<T: Transaction>(&self, tx: T) -> Result<TxId> {
        Ok(self.uncached_client().submit(&tx.into()).await?)
    }

    #[cfg(feature = "coin-cache")]
    async fn find_in_cache<'a>(
        &self,
        coin_ids: impl IntoIterator<Item = (&'a (Bech32Address, AssetId), &'a Vec<CoinTypeId>)>,
    ) -> Option<((Bech32Address, AssetId), CoinTypeId)> {
        let mut locked_cache = self.coins_cache.lock().await;

        for (key, ids) in coin_ids {
            let items = locked_cache.get_active(key);

            if items.is_empty() {
                continue;
            }

            for id in ids {
                if items.contains(id) {
                    return Some((key.clone(), id.clone()));
                }
            }
        }

        None
    }

    #[cfg(feature = "coin-cache")]
    async fn check_inputs_already_in_cache<'a>(
        &self,
        coin_ids: impl IntoIterator<Item = (&'a (Bech32Address, AssetId), &'a Vec<CoinTypeId>)>,
    ) -> Result<()> {
        use fuels_core::types::errors::{transaction, Error};

        if let Some(((addr, asset_id), coin_type_id)) = self.find_in_cache(coin_ids).await {
            let msg = match coin_type_id {
                CoinTypeId::UtxoId(utxo_id) => format!("coin with utxo_id: `{utxo_id:x}`"),
                CoinTypeId::Nonce(nonce) => format!("message with nonce: `{nonce}`"),
            };
            Err(Error::Transaction(transaction::Reason::Validation(
                format!("{msg} was submitted recently in a transaction - attempting to spend it again will result in an error. Wallet address: `{addr}`, asset id: `{asset_id}`"),
            )))
        } else {
            Ok(())
        }
    }

    #[cfg(feature = "coin-cache")]
    async fn submit<T: Transaction>(&self, tx: T) -> Result<TxId> {
        let consensus_parameters = self.consensus_parameters().await?;
        let base_asset_id = consensus_parameters.base_asset_id();

        let used_utxos = tx.used_coins(base_asset_id);
        self.check_inputs_already_in_cache(&used_utxos).await?;

        let tx_id = self.uncached_client().submit(&tx.into()).await?;
        self.coins_cache.lock().await.insert_multiple(used_utxos);

        Ok(tx_id)
    }

    pub async fn tx_status(&self, tx_id: &TxId) -> Result<TxStatus> {
        Ok(self
            .uncached_client()
            .transaction_status(tx_id)
            .await?
            .into())
    }

    pub async fn chain_info(&self) -> Result<ChainInfo> {
        Ok(self.uncached_client().chain_info().await?.into())
    }

    pub async fn consensus_parameters(&self) -> Result<ConsensusParameters> {
        self.cached_client.consensus_parameters().await
    }

    pub async fn node_info(&self) -> Result<NodeInfo> {
        Ok(self.uncached_client().node_info().await?.into())
    }

    pub async fn latest_gas_price(&self) -> Result<LatestGasPrice> {
        Ok(self.uncached_client().latest_gas_price().await?)
    }

    pub async fn estimate_gas_price(&self, block_horizon: u32) -> Result<EstimateGasPrice> {
        Ok(self
            .uncached_client()
            .estimate_gas_price(block_horizon)
            .await?)
    }

    pub async fn dry_run(&self, tx: impl Transaction) -> Result<TxStatus> {
        let [tx_status] = self
            .uncached_client()
            .dry_run(Transactions::new().insert(tx).as_slice())
            .await?
            .into_iter()
            .map(Into::into)
            .collect::<Vec<_>>()
            .try_into()
            .expect("should have only one element");

        Ok(tx_status)
    }

    pub async fn dry_run_multiple(
        &self,
        transactions: Transactions,
    ) -> Result<Vec<(TxId, TxStatus)>> {
        Ok(self
            .uncached_client()
            .dry_run(transactions.as_slice())
            .await?
            .into_iter()
            .map(|execution_status| (execution_status.id, execution_status.into()))
            .collect())
    }

    pub async fn dry_run_opt(
        &self,
        tx: impl Transaction,
        utxo_validation: bool,
        gas_price: Option<u64>,
    ) -> Result<TxStatus> {
        let [tx_status] = self
            .uncached_client()
            .dry_run_opt(
                Transactions::new().insert(tx).as_slice(),
                Some(utxo_validation),
                gas_price,
            )
            .await?
            .into_iter()
            .map(Into::into)
            .collect::<Vec<_>>()
            .try_into()
            .expect("should have only one element");

        Ok(tx_status)
    }

    pub async fn dry_run_opt_multiple(
        &self,
        transactions: Transactions,
        utxo_validation: bool,
        gas_price: Option<u64>,
    ) -> Result<Vec<(TxId, TxStatus)>> {
        Ok(self
            .uncached_client()
            .dry_run_opt(transactions.as_slice(), Some(utxo_validation), gas_price)
            .await?
            .into_iter()
            .map(|execution_status| (execution_status.id, execution_status.into()))
            .collect())
    }

    /// Gets all unspent coins owned by address `from`, with asset ID `asset_id`.
    pub async fn get_coins(&self, from: &Bech32Address, asset_id: AssetId) -> Result<Vec<Coin>> {
        let mut coins: Vec<Coin> = vec![];
        let mut cursor = None;

        loop {
            let response = self
                .uncached_client()
                .coins(
                    &from.into(),
                    Some(&asset_id),
                    PaginationRequest {
                        cursor: cursor.clone(),
                        results: NUM_RESULTS_PER_REQUEST,
                        direction: PageDirection::Forward,
                    },
                )
                .await?;

            if response.results.is_empty() {
                break;
            }

            coins.extend(response.results.into_iter().map(Into::into));
            cursor = response.cursor;
        }

        Ok(coins)
    }

    async fn request_coins_to_spend(&self, filter: ResourceFilter) -> Result<Vec<CoinType>> {
        let queries = filter.resource_queries();

        let consensus_parameters = self.consensus_parameters().await?;
        let base_asset_id = *consensus_parameters.base_asset_id();

        let res = self
            .uncached_client()
            .coins_to_spend(
                &filter.owner(),
                queries.spend_query(base_asset_id),
                queries.exclusion_query(),
            )
            .await?
            .into_iter()
            .flatten()
            .map(CoinType::from)
            .collect();

        Ok(res)
    }

    /// Get some spendable coins of asset `asset_id` for address `from` that add up at least to
    /// amount `amount`. The returned coins (UTXOs) are actual coins that can be spent. The number
    /// of coins (UXTOs) is optimized to prevent dust accumulation.
    #[cfg(not(feature = "coin-cache"))]
    pub async fn get_spendable_resources(&self, filter: ResourceFilter) -> Result<Vec<CoinType>> {
        self.request_coins_to_spend(filter).await
    }

    /// Get some spendable coins of asset `asset_id` for address `from` that add up at least to
    /// amount `amount`. The returned coins (UTXOs) are actual coins that can be spent. The number
    /// of coins (UXTOs) is optimized to prevent dust accumulation.
    /// Coins that were recently submitted inside a tx will be ignored from the results.
    #[cfg(feature = "coin-cache")]
    pub async fn get_spendable_resources(
        &self,
        mut filter: ResourceFilter,
    ) -> Result<Vec<CoinType>> {
        self.extend_filter_with_cached(&mut filter).await?;

        self.request_coins_to_spend(filter).await
    }

    #[cfg(feature = "coin-cache")]
    async fn extend_filter_with_cached(&self, filter: &mut ResourceFilter) -> Result<()> {
        let consensus_parameters = self.consensus_parameters().await?;
        let mut cache = self.coins_cache.lock().await;
        let asset_id = filter
            .asset_id
            .unwrap_or(*consensus_parameters.base_asset_id());
        let used_coins = cache.get_active(&(filter.from.clone(), asset_id));

        let excluded_utxos = used_coins
            .iter()
            .filter_map(|coin_id| match coin_id {
                CoinTypeId::UtxoId(utxo_id) => Some(utxo_id),
                _ => None,
            })
            .cloned()
            .collect::<Vec<_>>();

        let excluded_message_nonces = used_coins
            .iter()
            .filter_map(|coin_id| match coin_id {
                CoinTypeId::Nonce(nonce) => Some(nonce),
                _ => None,
            })
            .cloned()
            .collect::<Vec<_>>();

        filter.excluded_utxos.extend(excluded_utxos);
        filter
            .excluded_message_nonces
            .extend(excluded_message_nonces);

        Ok(())
    }

    /// Get the balance of all spendable coins `asset_id` for address `address`. This is different
    /// from getting coins because we are just returning a number (the sum of UTXOs amount) instead
    /// of the UTXOs.
    pub async fn get_asset_balance(
        &self,
        address: &Bech32Address,
        asset_id: AssetId,
    ) -> Result<u64> {
        Ok(self
            .uncached_client()
            .balance(&address.into(), Some(&asset_id))
            .await?)
    }

    /// Get the balance of all spendable coins `asset_id` for contract with id `contract_id`.
    pub async fn get_contract_asset_balance(
        &self,
        contract_id: &Bech32ContractId,
        asset_id: AssetId,
    ) -> Result<u64> {
        Ok(self
            .uncached_client()
            .contract_balance(&contract_id.into(), Some(&asset_id))
            .await?)
    }

    /// Get all the spendable balances of all assets for address `address`. This is different from
    /// getting the coins because we are only returning the numbers (the sum of UTXOs coins amount
    /// for each asset id) and not the UTXOs coins themselves
    pub async fn get_balances(&self, address: &Bech32Address) -> Result<HashMap<String, u128>> {
        let mut balances = HashMap::new();
        let mut cursor = None;

        loop {
            let response = self
                .uncached_client()
                .balances(
                    &address.into(),
                    PaginationRequest {
                        cursor: cursor.clone(),
                        results: NUM_RESULTS_PER_REQUEST,
                        direction: PageDirection::Forward,
                    },
                )
                .await?;

            if response.results.is_empty() {
                break;
            }

            balances.extend(response.results.into_iter().map(
                |Balance {
                     owner: _,
                     amount,
                     asset_id,
                 }| (asset_id.to_string(), amount),
            ));
            cursor = response.cursor;
        }

        Ok(balances)
    }

    /// Get all balances of all assets for the contract with id `contract_id`.
    pub async fn get_contract_balances(
        &self,
        contract_id: &Bech32ContractId,
    ) -> Result<HashMap<AssetId, u64>> {
        let mut contract_balances = HashMap::new();
        let mut cursor = None;

        loop {
            let response = self
                .uncached_client()
                .contract_balances(
                    &contract_id.into(),
                    PaginationRequest {
                        cursor: cursor.clone(),
                        results: NUM_RESULTS_PER_REQUEST,
                        direction: PageDirection::Forward,
                    },
                )
                .await?;

            if response.results.is_empty() {
                break;
            }

            contract_balances.extend(response.results.into_iter().map(
                |ContractBalance {
                     contract: _,
                     amount,
                     asset_id,
                 }| (asset_id, amount),
            ));
            cursor = response.cursor;
        }

        Ok(contract_balances)
    }

    pub async fn get_transaction_by_id(&self, tx_id: &TxId) -> Result<Option<TransactionResponse>> {
        Ok(self
            .uncached_client()
            .transaction(tx_id)
            .await?
            .map(Into::into))
    }

    pub async fn get_transactions(
        &self,
        request: PaginationRequest<String>,
    ) -> Result<PaginatedResult<TransactionResponse, String>> {
        let pr = self.uncached_client().transactions(request).await?;

        Ok(PaginatedResult {
            cursor: pr.cursor,
            results: pr.results.into_iter().map(Into::into).collect(),
            has_next_page: pr.has_next_page,
            has_previous_page: pr.has_previous_page,
        })
    }

    // Get transaction(s) by owner
    pub async fn get_transactions_by_owner(
        &self,
        owner: &Bech32Address,
        request: PaginationRequest<String>,
    ) -> Result<PaginatedResult<TransactionResponse, String>> {
        let pr = self
            .uncached_client()
            .transactions_by_owner(&owner.into(), request)
            .await?;

        Ok(PaginatedResult {
            cursor: pr.cursor,
            results: pr.results.into_iter().map(Into::into).collect(),
            has_next_page: pr.has_next_page,
            has_previous_page: pr.has_previous_page,
        })
    }

    pub async fn latest_block_height(&self) -> Result<u32> {
        Ok(self.chain_info().await?.latest_block.header.height)
    }

    pub async fn latest_block_time(&self) -> Result<Option<DateTime<Utc>>> {
        Ok(self.chain_info().await?.latest_block.header.time)
    }

    pub async fn produce_blocks(
        &self,
        blocks_to_produce: u32,
        start_time: Option<DateTime<Utc>>,
    ) -> Result<u32> {
        let start_time = start_time.map(|time| Tai64::from_unix(time.timestamp()).0);

        Ok(self
            .uncached_client()
            .produce_blocks(blocks_to_produce, start_time)
            .await?
            .into())
    }

    pub async fn block(&self, block_id: &Bytes32) -> Result<Option<Block>> {
        Ok(self
            .uncached_client()
            .block(block_id)
            .await?
            .map(Into::into))
    }

    pub async fn block_by_height(&self, height: BlockHeight) -> Result<Option<Block>> {
        Ok(self
            .uncached_client()
            .block_by_height(height)
            .await?
            .map(Into::into))
    }

    // - Get block(s)
    pub async fn get_blocks(
        &self,
        request: PaginationRequest<String>,
    ) -> Result<PaginatedResult<Block, String>> {
        let pr = self.uncached_client().blocks(request).await?;

        Ok(PaginatedResult {
            cursor: pr.cursor,
            results: pr.results.into_iter().map(Into::into).collect(),
            has_next_page: pr.has_next_page,
            has_previous_page: pr.has_previous_page,
        })
    }

    pub async fn estimate_transaction_cost<T: Transaction>(
        &self,
        mut tx: T,
        tolerance: Option<f64>,
        block_horizon: Option<u32>,
    ) -> Result<TransactionCost> {
        let block_horizon = block_horizon.unwrap_or(DEFAULT_GAS_ESTIMATION_BLOCK_HORIZON);
        let tolerance = tolerance.unwrap_or(DEFAULT_GAS_ESTIMATION_TOLERANCE);

        let EstimateGasPrice { gas_price, .. } = self.estimate_gas_price(block_horizon).await?;

        let gas_used = self
            .get_gas_used_with_tolerance(tx.clone(), tolerance)
            .await?;

        if tx.is_using_predicates() {
            tx.estimate_predicates(self, None).await?;
        }

        let transaction_fee = tx
            .clone()
            .fee_checked_from_tx(&self.consensus_parameters().await?, gas_price)
            .expect("Error calculating TransactionFee");

        Ok(TransactionCost {
            gas_price,
            gas_used,
            metered_bytes_size: tx.metered_bytes_size() as u64,
            total_fee: transaction_fee.max_fee(),
        })
    }

    // Increase estimated gas by the provided tolerance
    async fn get_gas_used_with_tolerance<T: Transaction>(
        &self,
        tx: T,
        tolerance: f64,
    ) -> Result<u64> {
        let receipts = self.dry_run_opt(tx, false, None).await?.take_receipts();
        let gas_used = self.get_script_gas_used(&receipts);

        Ok((gas_used as f64 * (1.0 + tolerance)).ceil() as u64)
    }

    fn get_script_gas_used(&self, receipts: &[Receipt]) -> u64 {
        receipts
            .iter()
            .rfind(|r| matches!(r, Receipt::ScriptResult { .. }))
            .map(|script_result| {
                script_result
                    .gas_used()
                    .expect("could not retrieve gas used from ScriptResult")
            })
            .unwrap_or(0)
    }

    pub async fn get_messages(&self, from: &Bech32Address) -> Result<Vec<Message>> {
        let mut messages = Vec::new();
        let mut cursor = None;

        loop {
            let response = self
                .uncached_client()
                .messages(
                    Some(&from.into()),
                    PaginationRequest {
                        cursor: cursor.clone(),
                        results: NUM_RESULTS_PER_REQUEST,
                        direction: PageDirection::Forward,
                    },
                )
                .await?;

            if response.results.is_empty() {
                break;
            }

            messages.extend(response.results.into_iter().map(Into::into));
            cursor = response.cursor;
        }

        Ok(messages)
    }

    pub async fn get_message_proof(
        &self,
        tx_id: &TxId,
        nonce: &Nonce,
        commit_block_id: Option<&Bytes32>,
        commit_block_height: Option<u32>,
    ) -> Result<MessageProof> {
        self.uncached_client()
            .message_proof(
                tx_id,
                nonce,
                commit_block_id.map(Into::into),
                commit_block_height.map(Into::into),
            )
            .await
            .map(Into::into)
            .map_err(Into::into)
    }

    pub async fn is_user_account(&self, address: impl Into<Bytes32>) -> Result<bool> {
        self.uncached_client()
            .is_user_account(*address.into())
            .await
    }

    pub fn with_retry_config(mut self, retry_config: RetryConfig) -> Self {
        self.uncached_client_mut().set_retry_config(retry_config);

        self
    }

    pub async fn contract_exists(&self, contract_id: &Bech32ContractId) -> Result<bool> {
        Ok(self
            .uncached_client()
            .contract_exists(&contract_id.into())
            .await?)
    }

    fn uncached_client(&self) -> &RetryableClient {
        self.cached_client.inner()
    }

    fn uncached_client_mut(&mut self) -> &mut RetryableClient {
        self.cached_client.inner_mut()
    }
}

#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
impl DryRunner for Provider {
    async fn dry_run(&self, tx: FuelTransaction) -> Result<DryRun> {
        let [tx_execution_status] = self
            .uncached_client()
            .dry_run_opt(&vec![tx], Some(false), Some(0))
            .await?
            .try_into()
            .expect("should have only one element");

        let receipts = tx_execution_status.result.receipts();
        let script_gas = self.get_script_gas_used(receipts);

        let variable_outputs = receipts
            .iter()
            .filter(
                |receipt| matches!(receipt, Receipt::TransferOut { amount, .. } if *amount != 0),
            )
            .count();

        let succeeded = matches!(
            tx_execution_status.result,
            TransactionExecutionResult::Success { .. }
        );

        let dry_run = DryRun {
            succeeded,
            script_gas,
            variable_outputs,
        };

        Ok(dry_run)
    }

    async fn estimate_gas_price(&self, block_horizon: u32) -> Result<u64> {
        Ok(self.estimate_gas_price(block_horizon).await?.gas_price)
    }

    async fn estimate_predicates(
        &self,
        tx: &FuelTransaction,
        _latest_chain_executor_version: Option<u32>,
    ) -> Result<FuelTransaction> {
        Ok(self.uncached_client().estimate_predicates(tx).await?)
    }

    async fn consensus_parameters(&self) -> Result<ConsensusParameters> {
        Provider::consensus_parameters(self).await
    }
}\n```

The example uses default values for the asset ID and the exclusion lists. This resolves to the base asset ID and empty vectors for the ID lists respectively:

```rust\n#[cfg(test)]
mod tests {
    use std::time::Duration;

    use fuels::prelude::Result;

    #[ignore = "testnet currently not compatible with the sdk"]
    #[tokio::test]
    async fn connect_to_fuel_node() -> Result<()> {
        // ANCHOR: connect_to_testnet
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Create a provider pointing to the testnet.
        let provider = Provider::connect("testnet.fuel.network").await.unwrap();

        // Setup a private key
        let secret = SecretKey::from_str(
            "a1447cd75accc6b71a976fd3401a1f6ce318d27ba660b0315ee6ac347bf39568",
        )?;

        // Create the wallet
        let wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));

        // Get the wallet address. Used later with the faucet
        dbg!(wallet.address().to_string());
        // ANCHOR_END: connect_to_testnet

        let provider = setup_test_provider(vec![], vec![], None, None).await?;
        let port = provider.url().split(':').last().unwrap();

        // ANCHOR: local_node_address
        let _provider = Provider::connect(format!("127.0.0.1:{port}")).await?;
        // ANCHOR_END: local_node_address

        Ok(())
    }

    #[tokio::test]
    async fn query_the_blockchain() -> Result<()> {
        // ANCHOR: setup_test_blockchain
        use fuels::prelude::*;

        // Set up our test blockchain.

        // Create a random wallet (more on wallets later).
        // ANCHOR: setup_single_asset
        let wallet = WalletUnlocked::new_random(None);

        // How many coins in our wallet.
        let number_of_coins = 1;

        // The amount/value in each coin in our wallet.
        let amount_per_coin = 3;

        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            number_of_coins,
            amount_per_coin,
        );
        // ANCHOR_END: setup_single_asset

        // ANCHOR: configure_retry
        let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?;
        let provider = setup_test_provider(coins.clone(), vec![], None, None)
            .await?
            .with_retry_config(retry_config);
        // ANCHOR_END: configure_retry
        // ANCHOR_END: setup_test_blockchain

        // ANCHOR: get_coins
        let consensus_parameters = provider.consensus_parameters().await?;
        let coins = provider
            .get_coins(wallet.address(), *consensus_parameters.base_asset_id())
            .await?;
        assert_eq!(coins.len(), 1);
        // ANCHOR_END: get_coins

        // ANCHOR: get_spendable_resources
        let filter = ResourceFilter {
            from: wallet.address().clone(),
            amount: 1,
            ..Default::default()
        };
        let spendable_resources = provider.get_spendable_resources(filter).await?;
        assert_eq!(spendable_resources.len(), 1);
        // ANCHOR_END: get_spendable_resources

        // ANCHOR: get_balances
        let _balances = provider.get_balances(wallet.address()).await?;
        // ANCHOR_END: get_balances

        Ok(())
    }
}\n```

Get balances from an address

Get all the spendable balances of all assets for an address. This is different from getting the coins because we only return the numbers (the sum of UTXOs coins amount for each asset ID) and not the UTXOs coins themselves.

```rust\n#[cfg(test)]
mod tests {
    use std::time::Duration;

    use fuels::prelude::Result;

    #[ignore = "testnet currently not compatible with the sdk"]
    #[tokio::test]
    async fn connect_to_fuel_node() -> Result<()> {
        // ANCHOR: connect_to_testnet
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Create a provider pointing to the testnet.
        let provider = Provider::connect("testnet.fuel.network").await.unwrap();

        // Setup a private key
        let secret = SecretKey::from_str(
            "a1447cd75accc6b71a976fd3401a1f6ce318d27ba660b0315ee6ac347bf39568",
        )?;

        // Create the wallet
        let wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));

        // Get the wallet address. Used later with the faucet
        dbg!(wallet.address().to_string());
        // ANCHOR_END: connect_to_testnet

        let provider = setup_test_provider(vec![], vec![], None, None).await?;
        let port = provider.url().split(':').last().unwrap();

        // ANCHOR: local_node_address
        let _provider = Provider::connect(format!("127.0.0.1:{port}")).await?;
        // ANCHOR_END: local_node_address

        Ok(())
    }

    #[tokio::test]
    async fn query_the_blockchain() -> Result<()> {
        // ANCHOR: setup_test_blockchain
        use fuels::prelude::*;

        // Set up our test blockchain.

        // Create a random wallet (more on wallets later).
        // ANCHOR: setup_single_asset
        let wallet = WalletUnlocked::new_random(None);

        // How many coins in our wallet.
        let number_of_coins = 1;

        // The amount/value in each coin in our wallet.
        let amount_per_coin = 3;

        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            number_of_coins,
            amount_per_coin,
        );
        // ANCHOR_END: setup_single_asset

        // ANCHOR: configure_retry
        let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?;
        let provider = setup_test_provider(coins.clone(), vec![], None, None)
            .await?
            .with_retry_config(retry_config);
        // ANCHOR_END: configure_retry
        // ANCHOR_END: setup_test_blockchain

        // ANCHOR: get_coins
        let consensus_parameters = provider.consensus_parameters().await?;
        let coins = provider
            .get_coins(wallet.address(), *consensus_parameters.base_asset_id())
            .await?;
        assert_eq!(coins.len(), 1);
        // ANCHOR_END: get_coins

        // ANCHOR: get_spendable_resources
        let filter = ResourceFilter {
            from: wallet.address().clone(),
            amount: 1,
            ..Default::default()
        };
        let spendable_resources = provider.get_spendable_resources(filter).await?;
        assert_eq!(spendable_resources.len(), 1);
        // ANCHOR_END: get_spendable_resources

        // ANCHOR: get_balances
        let _balances = provider.get_balances(wallet.address()).await?;
        // ANCHOR_END: get_balances

        Ok(())
    }
}\n```

Retrying requests

The Provider can be configured to retry a request upon receiving a io::Error.

Note: Currently all node errors are received as io::Errors. So, if configured, a retry will happen even if, for example, a transaction failed to verify.

We can configure the number of retry attempts and the retry strategy as detailed below.

RetryConfig

The retry behavior can be altered by giving a custom RetryConfig. It allows for configuring the maximum number of attempts and the interval strategy used.

```rust\nuse std::{fmt::Debug, future::Future, num::NonZeroU32, time::Duration};

use fuels_core::types::errors::{error, Result};

/// A set of strategies to control retry intervals between attempts.
///
/// The `Backoff` enum defines different strategies for managing intervals between retry attempts.
/// Each strategy allows you to customize the waiting time before a new attempt based on the
/// number of attempts made.
///
/// # Variants
///
/// - `Linear(Duration)`: Increases the waiting time linearly with each attempt.
/// - `Exponential(Duration)`: Doubles the waiting time with each attempt.
/// - `Fixed(Duration)`: Uses a constant waiting time between attempts.
///
/// # Examples
///
/// ```rust
/// use std::time::Duration;
/// use fuels_accounts::provider::Backoff;
///
/// let linear_backoff = Backoff::Linear(Duration::from_secs(2));
/// let exponential_backoff = Backoff::Exponential(Duration::from_secs(1));
/// let fixed_backoff = Backoff::Fixed(Duration::from_secs(5));
/// ```
//ANCHOR: backoff
#[derive(Debug, Clone)]
pub enum Backoff {
    Linear(Duration),
    Exponential(Duration),
    Fixed(Duration),
}
//ANCHOR_END: backoff

impl Default for Backoff {
    fn default() -> Self {
        Backoff::Linear(Duration::from_millis(10))
    }
}

impl Backoff {
    pub fn wait_duration(&self, attempt: u32) -> Duration {
        match self {
            Backoff::Linear(base_duration) => *base_duration * (attempt + 1),
            Backoff::Exponential(base_duration) => *base_duration * 2u32.pow(attempt),
            Backoff::Fixed(interval) => *interval,
        }
    }
}

/// Configuration for controlling retry behavior.
///
/// The `RetryConfig` struct encapsulates the configuration parameters for controlling the retry behavior
/// of asynchronous actions. It includes the maximum number of attempts and the interval strategy from
/// the `Backoff` enum that determines how much time to wait between retry attempts.
///
/// # Fields
///
/// - `max_attempts`: The maximum number of attempts before giving up.
/// - `interval`: The chosen interval strategy from the `Backoff` enum.
///
/// # Examples
///
/// ```rust
/// use std::num::NonZeroUsize;
/// use std::time::Duration;
/// use fuels_accounts::provider::{Backoff, RetryConfig};
///
/// let max_attempts = 5;
/// let interval_strategy = Backoff::Exponential(Duration::from_secs(1));
///
/// let retry_config = RetryConfig::new(max_attempts, interval_strategy).unwrap();
/// ```
// ANCHOR: retry_config
#[derive(Clone, Debug)]
pub struct RetryConfig {
    max_attempts: NonZeroU32,
    interval: Backoff,
}
// ANCHOR_END: retry_config

impl RetryConfig {
    pub fn new(max_attempts: u32, interval: Backoff) -> Result<Self> {
        let max_attempts = NonZeroU32::new(max_attempts)
            .ok_or_else(|| error!(Other, "`max_attempts` must be greater than `0`"))?;

        Ok(RetryConfig {
            max_attempts,
            interval,
        })
    }
}

impl Default for RetryConfig {
    fn default() -> Self {
        Self {
            max_attempts: NonZeroU32::new(1).expect("should not fail"),
            interval: Default::default(),
        }
    }
}

/// Retries an asynchronous action with customizable retry behavior.
///
/// This function takes an asynchronous action represented by a closure `action`.
/// The action is executed repeatedly with backoff and retry logic based on the
/// provided `retry_config` and the `should_retry` condition.
///
/// The `action` closure should return a `Future` that resolves to a `Result<T, K>`,
/// where `T` represents the success type and `K` represents the error type.
///
/// # Parameters
///
/// - `action`: The asynchronous action to be retried.
/// - `retry_config`: A reference to the retry configuration.
/// - `should_retry`: A closure that determines whether to retry based on the result.
///
/// # Return
///
/// Returns `Ok(T)` if the action succeeds without requiring further retries.
/// Returns `Err(Error)` if the maximum number of attempts is reached and the action
/// still fails. If a retryable error occurs during the attempts, the error will
/// be returned if the `should_retry` condition allows further retries.
pub(crate) async fn retry<Fut, T, ShouldRetry>(
    mut action: impl FnMut() -> Fut,
    retry_config: &RetryConfig,
    should_retry: ShouldRetry,
) -> T
where
    Fut: Future<Output = T>,
    ShouldRetry: Fn(&T) -> bool,
{
    let mut last_result = None;

    for attempt in 0..retry_config.max_attempts.into() {
        let result = action().await;

        if should_retry(&result) {
            last_result = Some(result)
        } else {
            return result;
        }

        tokio::time::sleep(retry_config.interval.wait_duration(attempt)).await;
    }

    last_result.expect("should not happen")
}

#[cfg(test)]
mod tests {
    mod retry_until {
        use std::time::{Duration, Instant};

        use fuels_core::types::errors::{error, Result};
        use tokio::sync::Mutex;

        use crate::provider::{retry_util, Backoff, RetryConfig};

        #[tokio::test]
        async fn returns_last_received_response() -> Result<()> {
            // given
            let err_msgs = ["err1", "err2", "err3"];
            let number_of_attempts = Mutex::new(0usize);

            let will_always_fail = || async {
                let msg = err_msgs[*number_of_attempts.lock().await];
                *number_of_attempts.lock().await += 1;

                msg
            };

            let should_retry_fn = |_res: &_| -> bool { true };

            let retry_options = RetryConfig::new(3, Backoff::Linear(Duration::from_millis(10)))?;

            // when
            let response =
                retry_util::retry(will_always_fail, &retry_options, should_retry_fn).await;

            // then
            assert_eq!(response, "err3");

            Ok(())
        }

        #[tokio::test]
        async fn stops_retrying_when_predicate_is_satisfied() -> Result<()> {
            // given
            let values = Mutex::new(vec![1, 2, 3]);

            let will_always_fail = || async { values.lock().await.pop().unwrap() };

            let should_retry_fn = |res: &i32| *res != 2;

            let retry_options = RetryConfig::new(3, Backoff::Linear(Duration::from_millis(10)))?;

            // when
            let response =
                retry_util::retry(will_always_fail, &retry_options, should_retry_fn).await;

            // then
            assert_eq!(response, 2);

            Ok(())
        }

        #[tokio::test]
        async fn retry_respects_delay_between_attempts_fixed() -> Result<()> {
            // given
            let timestamps: Mutex<Vec<Instant>> = Mutex::new(vec![]);

            let will_fail_and_record_timestamp = || async {
                timestamps.lock().await.push(Instant::now());
                Result::<()>::Err(error!(Other, "error"))
            };

            let should_retry_fn = |_res: &_| -> bool { true };

            let retry_options = RetryConfig::new(3, Backoff::Fixed(Duration::from_millis(100)))?;

            // when
            let _ = retry_util::retry(
                will_fail_and_record_timestamp,
                &retry_options,
                should_retry_fn,
            )
            .await;

            // then
            let timestamps_vec = timestamps.lock().await.clone();

            let timestamps_spaced_out_at_least_100_mills = timestamps_vec
                .iter()
                .zip(timestamps_vec.iter().skip(1))
                .all(|(current_timestamp, the_next_timestamp)| {
                    the_next_timestamp.duration_since(*current_timestamp)
                        >= Duration::from_millis(100)
                });

            assert!(
                timestamps_spaced_out_at_least_100_mills,
                "retry did not wait for the specified time between attempts"
            );

            Ok(())
        }

        #[tokio::test]
        async fn retry_respects_delay_between_attempts_linear() -> Result<()> {
            // given
            let timestamps: Mutex<Vec<Instant>> = Mutex::new(vec![]);

            let will_fail_and_record_timestamp = || async {
                timestamps.lock().await.push(Instant::now());
                Result::<()>::Err(error!(Other, "error"))
            };

            let should_retry_fn = |_res: &_| -> bool { true };

            let retry_options = RetryConfig::new(3, Backoff::Linear(Duration::from_millis(100)))?;

            // when
            let _ = retry_util::retry(
                will_fail_and_record_timestamp,
                &retry_options,
                should_retry_fn,
            )
            .await;

            // then
            let timestamps_vec = timestamps.lock().await.clone();

            let timestamps_spaced_out_at_least_100_mills = timestamps_vec
                .iter()
                .zip(timestamps_vec.iter().skip(1))
                .enumerate()
                .all(|(attempt, (current_timestamp, the_next_timestamp))| {
                    the_next_timestamp.duration_since(*current_timestamp)
                        >= (Duration::from_millis(100) * (attempt + 1) as u32)
                });

            assert!(
                timestamps_spaced_out_at_least_100_mills,
                "retry did not wait for the specified time between attempts"
            );

            Ok(())
        }

        #[tokio::test]
        async fn retry_respects_delay_between_attempts_exponential() -> Result<()> {
            // given
            let timestamps: Mutex<Vec<Instant>> = Mutex::new(vec![]);

            let will_fail_and_record_timestamp = || async {
                timestamps.lock().await.push(Instant::now());
                Result::<()>::Err(error!(Other, "error"))
            };

            let should_retry_fn = |_res: &_| -> bool { true };

            let retry_options =
                RetryConfig::new(3, Backoff::Exponential(Duration::from_millis(100)))?;

            // when
            let _ = retry_util::retry(
                will_fail_and_record_timestamp,
                &retry_options,
                should_retry_fn,
            )
            .await;

            // then
            let timestamps_vec = timestamps.lock().await.clone();

            let timestamps_spaced_out_at_least_100_mills = timestamps_vec
                .iter()
                .zip(timestamps_vec.iter().skip(1))
                .enumerate()
                .all(|(attempt, (current_timestamp, the_next_timestamp))| {
                    the_next_timestamp.duration_since(*current_timestamp)
                        >= (Duration::from_millis(100) * (2_usize.pow((attempt) as u32)) as u32)
                });

            assert!(
                timestamps_spaced_out_at_least_100_mills,
                "retry did not wait for the specified time between attempts"
            );

            Ok(())
        }
    }
}\n```
```rust\n#[cfg(test)]
mod tests {
    use std::time::Duration;

    use fuels::prelude::Result;

    #[ignore = "testnet currently not compatible with the sdk"]
    #[tokio::test]
    async fn connect_to_fuel_node() -> Result<()> {
        // ANCHOR: connect_to_testnet
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Create a provider pointing to the testnet.
        let provider = Provider::connect("testnet.fuel.network").await.unwrap();

        // Setup a private key
        let secret = SecretKey::from_str(
            "a1447cd75accc6b71a976fd3401a1f6ce318d27ba660b0315ee6ac347bf39568",
        )?;

        // Create the wallet
        let wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));

        // Get the wallet address. Used later with the faucet
        dbg!(wallet.address().to_string());
        // ANCHOR_END: connect_to_testnet

        let provider = setup_test_provider(vec![], vec![], None, None).await?;
        let port = provider.url().split(':').last().unwrap();

        // ANCHOR: local_node_address
        let _provider = Provider::connect(format!("127.0.0.1:{port}")).await?;
        // ANCHOR_END: local_node_address

        Ok(())
    }

    #[tokio::test]
    async fn query_the_blockchain() -> Result<()> {
        // ANCHOR: setup_test_blockchain
        use fuels::prelude::*;

        // Set up our test blockchain.

        // Create a random wallet (more on wallets later).
        // ANCHOR: setup_single_asset
        let wallet = WalletUnlocked::new_random(None);

        // How many coins in our wallet.
        let number_of_coins = 1;

        // The amount/value in each coin in our wallet.
        let amount_per_coin = 3;

        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            number_of_coins,
            amount_per_coin,
        );
        // ANCHOR_END: setup_single_asset

        // ANCHOR: configure_retry
        let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?;
        let provider = setup_test_provider(coins.clone(), vec![], None, None)
            .await?
            .with_retry_config(retry_config);
        // ANCHOR_END: configure_retry
        // ANCHOR_END: setup_test_blockchain

        // ANCHOR: get_coins
        let consensus_parameters = provider.consensus_parameters().await?;
        let coins = provider
            .get_coins(wallet.address(), *consensus_parameters.base_asset_id())
            .await?;
        assert_eq!(coins.len(), 1);
        // ANCHOR_END: get_coins

        // ANCHOR: get_spendable_resources
        let filter = ResourceFilter {
            from: wallet.address().clone(),
            amount: 1,
            ..Default::default()
        };
        let spendable_resources = provider.get_spendable_resources(filter).await?;
        assert_eq!(spendable_resources.len(), 1);
        // ANCHOR_END: get_spendable_resources

        // ANCHOR: get_balances
        let _balances = provider.get_balances(wallet.address()).await?;
        // ANCHOR_END: get_balances

        Ok(())
    }
}\n```

Interval strategy - Backoff

Backoff defines different strategies for managing intervals between retry attempts. Each strategy allows you to customize the waiting time before a new attempt based on the number of attempts made.

Variants

  • Linear(Duration): Default Increases the waiting time linearly with each attempt.
  • Exponential(Duration): Doubles the waiting time with each attempt.
  • Fixed(Duration): Uses a constant waiting time between attempts.
```rust\nuse std::{fmt::Debug, future::Future, num::NonZeroU32, time::Duration};

use fuels_core::types::errors::{error, Result};

/// A set of strategies to control retry intervals between attempts.
///
/// The `Backoff` enum defines different strategies for managing intervals between retry attempts.
/// Each strategy allows you to customize the waiting time before a new attempt based on the
/// number of attempts made.
///
/// # Variants
///
/// - `Linear(Duration)`: Increases the waiting time linearly with each attempt.
/// - `Exponential(Duration)`: Doubles the waiting time with each attempt.
/// - `Fixed(Duration)`: Uses a constant waiting time between attempts.
///
/// # Examples
///
/// ```rust
/// use std::time::Duration;
/// use fuels_accounts::provider::Backoff;
///
/// let linear_backoff = Backoff::Linear(Duration::from_secs(2));
/// let exponential_backoff = Backoff::Exponential(Duration::from_secs(1));
/// let fixed_backoff = Backoff::Fixed(Duration::from_secs(5));
/// ```
//ANCHOR: backoff
#[derive(Debug, Clone)]
pub enum Backoff {
    Linear(Duration),
    Exponential(Duration),
    Fixed(Duration),
}
//ANCHOR_END: backoff

impl Default for Backoff {
    fn default() -> Self {
        Backoff::Linear(Duration::from_millis(10))
    }
}

impl Backoff {
    pub fn wait_duration(&self, attempt: u32) -> Duration {
        match self {
            Backoff::Linear(base_duration) => *base_duration * (attempt + 1),
            Backoff::Exponential(base_duration) => *base_duration * 2u32.pow(attempt),
            Backoff::Fixed(interval) => *interval,
        }
    }
}

/// Configuration for controlling retry behavior.
///
/// The `RetryConfig` struct encapsulates the configuration parameters for controlling the retry behavior
/// of asynchronous actions. It includes the maximum number of attempts and the interval strategy from
/// the `Backoff` enum that determines how much time to wait between retry attempts.
///
/// # Fields
///
/// - `max_attempts`: The maximum number of attempts before giving up.
/// - `interval`: The chosen interval strategy from the `Backoff` enum.
///
/// # Examples
///
/// ```rust
/// use std::num::NonZeroUsize;
/// use std::time::Duration;
/// use fuels_accounts::provider::{Backoff, RetryConfig};
///
/// let max_attempts = 5;
/// let interval_strategy = Backoff::Exponential(Duration::from_secs(1));
///
/// let retry_config = RetryConfig::new(max_attempts, interval_strategy).unwrap();
/// ```
// ANCHOR: retry_config
#[derive(Clone, Debug)]
pub struct RetryConfig {
    max_attempts: NonZeroU32,
    interval: Backoff,
}
// ANCHOR_END: retry_config

impl RetryConfig {
    pub fn new(max_attempts: u32, interval: Backoff) -> Result<Self> {
        let max_attempts = NonZeroU32::new(max_attempts)
            .ok_or_else(|| error!(Other, "`max_attempts` must be greater than `0`"))?;

        Ok(RetryConfig {
            max_attempts,
            interval,
        })
    }
}

impl Default for RetryConfig {
    fn default() -> Self {
        Self {
            max_attempts: NonZeroU32::new(1).expect("should not fail"),
            interval: Default::default(),
        }
    }
}

/// Retries an asynchronous action with customizable retry behavior.
///
/// This function takes an asynchronous action represented by a closure `action`.
/// The action is executed repeatedly with backoff and retry logic based on the
/// provided `retry_config` and the `should_retry` condition.
///
/// The `action` closure should return a `Future` that resolves to a `Result<T, K>`,
/// where `T` represents the success type and `K` represents the error type.
///
/// # Parameters
///
/// - `action`: The asynchronous action to be retried.
/// - `retry_config`: A reference to the retry configuration.
/// - `should_retry`: A closure that determines whether to retry based on the result.
///
/// # Return
///
/// Returns `Ok(T)` if the action succeeds without requiring further retries.
/// Returns `Err(Error)` if the maximum number of attempts is reached and the action
/// still fails. If a retryable error occurs during the attempts, the error will
/// be returned if the `should_retry` condition allows further retries.
pub(crate) async fn retry<Fut, T, ShouldRetry>(
    mut action: impl FnMut() -> Fut,
    retry_config: &RetryConfig,
    should_retry: ShouldRetry,
) -> T
where
    Fut: Future<Output = T>,
    ShouldRetry: Fn(&T) -> bool,
{
    let mut last_result = None;

    for attempt in 0..retry_config.max_attempts.into() {
        let result = action().await;

        if should_retry(&result) {
            last_result = Some(result)
        } else {
            return result;
        }

        tokio::time::sleep(retry_config.interval.wait_duration(attempt)).await;
    }

    last_result.expect("should not happen")
}

#[cfg(test)]
mod tests {
    mod retry_until {
        use std::time::{Duration, Instant};

        use fuels_core::types::errors::{error, Result};
        use tokio::sync::Mutex;

        use crate::provider::{retry_util, Backoff, RetryConfig};

        #[tokio::test]
        async fn returns_last_received_response() -> Result<()> {
            // given
            let err_msgs = ["err1", "err2", "err3"];
            let number_of_attempts = Mutex::new(0usize);

            let will_always_fail = || async {
                let msg = err_msgs[*number_of_attempts.lock().await];
                *number_of_attempts.lock().await += 1;

                msg
            };

            let should_retry_fn = |_res: &_| -> bool { true };

            let retry_options = RetryConfig::new(3, Backoff::Linear(Duration::from_millis(10)))?;

            // when
            let response =
                retry_util::retry(will_always_fail, &retry_options, should_retry_fn).await;

            // then
            assert_eq!(response, "err3");

            Ok(())
        }

        #[tokio::test]
        async fn stops_retrying_when_predicate_is_satisfied() -> Result<()> {
            // given
            let values = Mutex::new(vec![1, 2, 3]);

            let will_always_fail = || async { values.lock().await.pop().unwrap() };

            let should_retry_fn = |res: &i32| *res != 2;

            let retry_options = RetryConfig::new(3, Backoff::Linear(Duration::from_millis(10)))?;

            // when
            let response =
                retry_util::retry(will_always_fail, &retry_options, should_retry_fn).await;

            // then
            assert_eq!(response, 2);

            Ok(())
        }

        #[tokio::test]
        async fn retry_respects_delay_between_attempts_fixed() -> Result<()> {
            // given
            let timestamps: Mutex<Vec<Instant>> = Mutex::new(vec![]);

            let will_fail_and_record_timestamp = || async {
                timestamps.lock().await.push(Instant::now());
                Result::<()>::Err(error!(Other, "error"))
            };

            let should_retry_fn = |_res: &_| -> bool { true };

            let retry_options = RetryConfig::new(3, Backoff::Fixed(Duration::from_millis(100)))?;

            // when
            let _ = retry_util::retry(
                will_fail_and_record_timestamp,
                &retry_options,
                should_retry_fn,
            )
            .await;

            // then
            let timestamps_vec = timestamps.lock().await.clone();

            let timestamps_spaced_out_at_least_100_mills = timestamps_vec
                .iter()
                .zip(timestamps_vec.iter().skip(1))
                .all(|(current_timestamp, the_next_timestamp)| {
                    the_next_timestamp.duration_since(*current_timestamp)
                        >= Duration::from_millis(100)
                });

            assert!(
                timestamps_spaced_out_at_least_100_mills,
                "retry did not wait for the specified time between attempts"
            );

            Ok(())
        }

        #[tokio::test]
        async fn retry_respects_delay_between_attempts_linear() -> Result<()> {
            // given
            let timestamps: Mutex<Vec<Instant>> = Mutex::new(vec![]);

            let will_fail_and_record_timestamp = || async {
                timestamps.lock().await.push(Instant::now());
                Result::<()>::Err(error!(Other, "error"))
            };

            let should_retry_fn = |_res: &_| -> bool { true };

            let retry_options = RetryConfig::new(3, Backoff::Linear(Duration::from_millis(100)))?;

            // when
            let _ = retry_util::retry(
                will_fail_and_record_timestamp,
                &retry_options,
                should_retry_fn,
            )
            .await;

            // then
            let timestamps_vec = timestamps.lock().await.clone();

            let timestamps_spaced_out_at_least_100_mills = timestamps_vec
                .iter()
                .zip(timestamps_vec.iter().skip(1))
                .enumerate()
                .all(|(attempt, (current_timestamp, the_next_timestamp))| {
                    the_next_timestamp.duration_since(*current_timestamp)
                        >= (Duration::from_millis(100) * (attempt + 1) as u32)
                });

            assert!(
                timestamps_spaced_out_at_least_100_mills,
                "retry did not wait for the specified time between attempts"
            );

            Ok(())
        }

        #[tokio::test]
        async fn retry_respects_delay_between_attempts_exponential() -> Result<()> {
            // given
            let timestamps: Mutex<Vec<Instant>> = Mutex::new(vec![]);

            let will_fail_and_record_timestamp = || async {
                timestamps.lock().await.push(Instant::now());
                Result::<()>::Err(error!(Other, "error"))
            };

            let should_retry_fn = |_res: &_| -> bool { true };

            let retry_options =
                RetryConfig::new(3, Backoff::Exponential(Duration::from_millis(100)))?;

            // when
            let _ = retry_util::retry(
                will_fail_and_record_timestamp,
                &retry_options,
                should_retry_fn,
            )
            .await;

            // then
            let timestamps_vec = timestamps.lock().await.clone();

            let timestamps_spaced_out_at_least_100_mills = timestamps_vec
                .iter()
                .zip(timestamps_vec.iter().skip(1))
                .enumerate()
                .all(|(attempt, (current_timestamp, the_next_timestamp))| {
                    the_next_timestamp.duration_since(*current_timestamp)
                        >= (Duration::from_millis(100) * (2_usize.pow((attempt) as u32)) as u32)
                });

            assert!(
                timestamps_spaced_out_at_least_100_mills,
                "retry did not wait for the specified time between attempts"
            );

            Ok(())
        }
    }
}\n```

Accounts

The ViewOnlyAccount trait provides a common interface to query balances.

The Account trait, in addition to the above, also provides a common interface to retrieve spendable resources or transfer assets. When performing actions in the SDK that lead to a transaction, you will typically need to provide an account that will be used to allocate resources required by the transaction, including transaction fees.

The traits are implemented by the following types:

Transferring assets

An account implements the following methods for transferring assets:

  • transfer
  • force_transfer_to_contract
  • withdraw_to_base_layer

The following examples are provided for a Wallet account. A Predicate account would work similarly, but you might need to set its predicate data before attempting to spend resources owned by it.

With wallet.transfer you can initiate a transaction to transfer an asset from your account to a target address.

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

You can transfer assets to a contract via wallet.force_transfer_to_contract.

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

For transferring assets to the base layer chain, you can use wallet.withdraw_to_base_layer.

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

The above example creates an Address from a string and converts it to a Bech32Address. Next, it calls wallet.withdraw_to_base_layer by providing the address, the amount to be transferred, and the transaction policies. Lastly, to verify that the transfer succeeded, the relevant message proof is retrieved with provider.get_message_proof, and the amount and the recipient are verified.

Account impersonation

To facilitate account impersonation, the Rust SDK provides the ImpersonatedAccount struct. Since it implements Account, we can use it to simulate ownership of assets held by an account with a given address. This also implies that we can impersonate contract calls from that address. ImpersonatedAccount will only succeed in unlocking assets if the network is set up with utxo_validation = false.

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Managing wallets

You can use wallets for many important things, for instance:

  1. Checking your balance
  2. Transferring coins to a destination address or contract
  3. Signing messages and transactions
  4. Paying for network fees when sending transactions or deploying smart contracts

The SDK gives you many different ways to create and access wallets. Let's explore these different approaches in the following sub-chapters.

Note: Keep in mind that you should never share your private/secret key. And in the case of wallets that were derived from a mnemonic phrase, never share your mnemonic phrase. If you're planning on storing the wallet on disk, do not store the plain private/secret key and do not store the plain mnemonic phrase. Instead, use Wallet::encrypt to encrypt its content first before saving it to disk.

Creating a wallet from a private key

A new wallet with a randomly generated private key can be created by supplying Option<Provider>.

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

Alternatively, you can create a wallet from a predefined SecretKey.

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

Note: if None is supplied instead of a provider, any transaction related to the wallet will result in an error until a provider is linked with set_provider(). The optional parameter enables defining owners (wallet addresses) of genesis coins before a provider is launched.

Creating a wallet from mnemonic phrases

A mnemonic phrase is a cryptographically-generated sequence of words that's used to derive a private key. For instance: "oblige salon price punch saddle immune slogan rare snap desert retire surprise"; would generate the address 0xdf9d0e6c6c5f5da6e82e5e1a77974af6642bdb450a10c43f0c6910a212600185.

In addition to that, we also support Hierarchical Deterministic Wallets and derivation paths. You may recognize the string "m/44'/60'/0'/0/0" from somewhere; that's a derivation path. In simple terms, it's a way to derive many wallets from a single root wallet.

The SDK gives you two wallets from mnemonic instantiation methods: one that takes a derivation path (Wallet::new_from_mnemonic_phrase_with_path) and one that uses the default derivation path, in case you don't want or don't need to configure that (Wallet::new_from_mnemonic_phrase).

Here's how you can create wallets with both mnemonic phrases and derivation paths:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

Wallet Access

The kinds of operations we can perform with a Wallet instance depend on whether or not we have access to the wallet's private key.

In order to differentiate between Wallet instances that know their private key and those that do not, we use the WalletUnlocked and Wallet types respectively.

Wallet States

The WalletUnlocked type represents a wallet whose private key is known and stored internally in memory. A wallet must be of type WalletUnlocked in order to perform operations that involve signing messages or transactions.

You can learn more about signing here.

The Wallet type represents a wallet whose private key is not known or stored in memory. Instead, Wallet only knows its public address. A Wallet cannot be used to sign transactions, however it may still perform a whole suite of useful operations including listing transactions, assets, querying balances, and so on.

Note that the WalletUnlocked type provides a Deref implementation targeting its inner Wallet type. This means that all methods available on the Wallet type are also available on the WalletUnlocked type. In other words, WalletUnlocked can be thought of as a thin wrapper around Wallet that provides greater access via its private key.

Transitioning States

A Wallet instance can be unlocked by providing the private key:

let wallet_unlocked = wallet_locked.unlock(private_key);

A WalletUnlocked instance can be locked using the lock method:

let wallet_locked = wallet_unlocked.lock();

Most wallet constructors that create or generate a new wallet are provided on the WalletUnlocked type. Consider locking the wallet with the lock method after the new private key has been handled in order to reduce the scope in which the wallet's private key is stored in memory.

Design Guidelines

When designing APIs that accept a wallet as an input, we should think carefully about the kind of access that we require. API developers should aim to minimise their usage of WalletUnlocked in order to ensure private keys are stored in memory no longer than necessary to reduce the surface area for attacks and vulnerabilities in downstream libraries and applications.

Encrypting and storing wallets

Creating a wallet and storing an encrypted JSON wallet on disk

You can also manage a wallet using JSON wallets that are securely encrypted and stored on the disk. This makes it easier to manage multiple wallets, especially for testing purposes.

You can create a random wallet and, at the same time, encrypt and store it. Then, later, you can recover the wallet if you know the master password:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

Encrypting and storing a wallet created from a mnemonic or private key

If you have already created a wallet using a mnemonic phrase or a private key, you can also encrypt it and save it to disk:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

Checking balances and coins

In the Fuel network, each UTXO corresponds to a unique coin, and said coin has a corresponding amount (the same way a dollar bill has either 10$ or 5$ face value). So, when you want to query the balance for a given asset ID, you want to query the sum of the amount in each unspent coin. This querying is done very easily with a wallet:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

If you want to query all the balances (i.e., get the balance for each asset ID in that wallet), you can use the get_balances method:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

The return type is a HashMap, where the key is the asset ID's hex string, and the value is the corresponding balance. For example, we can get the base asset balance with:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

Setting up test wallets

You'll often want to create one or more test wallets when testing your contracts. Here's how to do it.

Setting up multiple test wallets

If you need multiple test wallets, they can be set up using the launch_custom_provider_and_get_wallets method.

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

You can customize your test wallets via WalletsConfig.

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

Note Wallets generated with launch_provider_and_get_wallet or launch_custom_provider_and_get_wallets will have deterministic addresses.

Setting up a test wallet with multiple random assets

You can create a test wallet containing multiple assets (including the base asset to pay for gas).

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```
  • coins: Vec<(UtxoId, Coin)> has num_assets * coins_per_assets coins (UTXOs)
  • asset_ids: Vec<AssetId> contains the num_assets randomly generated AssetIds (always includes the base asset)

Setting up a test wallet with multiple custom assets

You can also create assets with specific AssetIds, coin amounts, and number of coins.

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

This can also be achieved directly with the WalletsConfig.

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

Note In this case, you need to manually add the base asset and the corresponding number of coins and coin amount

Setting up assets

The Fuel blockchain holds many different assets; you can create your asset with its unique AssetId or create random assets for testing purposes.

You can use only one asset to pay for transaction fees and gas: the base asset, whose AssetId is 0x000...0, a 32-byte zeroed value.

For testing purposes, you can configure coins and amounts for assets. You can use setup_multiple_assets_coins:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

Note If setting up multiple assets, one of these assets will always be the base asset.

If you want to create coins only with the base asset, then you can use:

```rust\n#[cfg(test)]
mod tests {
    use std::time::Duration;

    use fuels::prelude::Result;

    #[ignore = "testnet currently not compatible with the sdk"]
    #[tokio::test]
    async fn connect_to_fuel_node() -> Result<()> {
        // ANCHOR: connect_to_testnet
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Create a provider pointing to the testnet.
        let provider = Provider::connect("testnet.fuel.network").await.unwrap();

        // Setup a private key
        let secret = SecretKey::from_str(
            "a1447cd75accc6b71a976fd3401a1f6ce318d27ba660b0315ee6ac347bf39568",
        )?;

        // Create the wallet
        let wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));

        // Get the wallet address. Used later with the faucet
        dbg!(wallet.address().to_string());
        // ANCHOR_END: connect_to_testnet

        let provider = setup_test_provider(vec![], vec![], None, None).await?;
        let port = provider.url().split(':').last().unwrap();

        // ANCHOR: local_node_address
        let _provider = Provider::connect(format!("127.0.0.1:{port}")).await?;
        // ANCHOR_END: local_node_address

        Ok(())
    }

    #[tokio::test]
    async fn query_the_blockchain() -> Result<()> {
        // ANCHOR: setup_test_blockchain
        use fuels::prelude::*;

        // Set up our test blockchain.

        // Create a random wallet (more on wallets later).
        // ANCHOR: setup_single_asset
        let wallet = WalletUnlocked::new_random(None);

        // How many coins in our wallet.
        let number_of_coins = 1;

        // The amount/value in each coin in our wallet.
        let amount_per_coin = 3;

        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            number_of_coins,
            amount_per_coin,
        );
        // ANCHOR_END: setup_single_asset

        // ANCHOR: configure_retry
        let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?;
        let provider = setup_test_provider(coins.clone(), vec![], None, None)
            .await?
            .with_retry_config(retry_config);
        // ANCHOR_END: configure_retry
        // ANCHOR_END: setup_test_blockchain

        // ANCHOR: get_coins
        let consensus_parameters = provider.consensus_parameters().await?;
        let coins = provider
            .get_coins(wallet.address(), *consensus_parameters.base_asset_id())
            .await?;
        assert_eq!(coins.len(), 1);
        // ANCHOR_END: get_coins

        // ANCHOR: get_spendable_resources
        let filter = ResourceFilter {
            from: wallet.address().clone(),
            amount: 1,
            ..Default::default()
        };
        let spendable_resources = provider.get_spendable_resources(filter).await?;
        assert_eq!(spendable_resources.len(), 1);
        // ANCHOR_END: get_spendable_resources

        // ANCHOR: get_balances
        let _balances = provider.get_balances(wallet.address()).await?;
        // ANCHOR_END: get_balances

        Ok(())
    }
}\n```

Note Choosing a large number of coins and assets for setup_multiple_assets_coins or setup_single_asset_coins can lead to considerable runtime for these methods. This will be improved in the future but for now, we recommend using up to 1_000_000 coins, or 1000 coins and assets simultaneously.

Signing

Once you've instantiated your wallet in an unlocked state using one of the previously discussed methods, you can sign a message with wallet.sign. Below is a full example of how to sign and recover a message.

```rust\nuse std::collections::HashMap;

use async_trait::async_trait;
use fuel_core_client::client::pagination::{PaginatedResult, PaginationRequest};
use fuel_tx::{Output, Receipt, TxId, TxPointer, UtxoId};
use fuel_types::{AssetId, Bytes32, ContractId, Nonce};
use fuels_core::types::{
    bech32::{Bech32Address, Bech32ContractId},
    coin::Coin,
    coin_type::CoinType,
    coin_type_id::CoinTypeId,
    errors::Result,
    input::Input,
    message::Message,
    transaction::{Transaction, TxPolicies},
    transaction_builders::{BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder},
    transaction_response::TransactionResponse,
};

use crate::{
    accounts_utils::{
        add_base_change_if_needed, available_base_assets_and_amount, calculate_missing_base_amount,
        extract_message_nonce, split_into_utxo_ids_and_nonces,
    },
    provider::{Provider, ResourceFilter},
};

#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait ViewOnlyAccount: std::fmt::Debug + Send + Sync + Clone {
    fn address(&self) -> &Bech32Address;

    fn try_provider(&self) -> Result<&Provider>;

    async fn get_transactions(
        &self,
        request: PaginationRequest<String>,
    ) -> Result<PaginatedResult<TransactionResponse, String>> {
        Ok(self
            .try_provider()?
            .get_transactions_by_owner(self.address(), request)
            .await?)
    }

    /// Gets all unspent coins of asset `asset_id` owned by the account.
    async fn get_coins(&self, asset_id: AssetId) -> Result<Vec<Coin>> {
        Ok(self
            .try_provider()?
            .get_coins(self.address(), asset_id)
            .await?)
    }

    /// Get the balance of all spendable coins `asset_id` for address `address`. This is different
    /// from getting coins because we are just returning a number (the sum of UTXOs amount) instead
    /// of the UTXOs.
    async fn get_asset_balance(&self, asset_id: &AssetId) -> Result<u64> {
        self.try_provider()?
            .get_asset_balance(self.address(), *asset_id)
            .await
    }

    /// Gets all unspent messages owned by the account.
    async fn get_messages(&self) -> Result<Vec<Message>> {
        Ok(self.try_provider()?.get_messages(self.address()).await?)
    }

    /// Get all the spendable balances of all assets for the account. This is different from getting
    /// the coins because we are only returning the sum of UTXOs coins amount and not the UTXOs
    /// coins themselves.
    async fn get_balances(&self) -> Result<HashMap<String, u128>> {
        self.try_provider()?.get_balances(self.address()).await
    }

    /// Get some spendable resources (coins and messages) of asset `asset_id` owned by the account
    /// that add up at least to amount `amount`. The returned coins (UTXOs) are actual coins that
    /// can be spent. The number of UXTOs is optimized to prevent dust accumulation.
    async fn get_spendable_resources(
        &self,
        asset_id: AssetId,
        amount: u64,
        excluded_coins: Option<Vec<CoinTypeId>>,
    ) -> Result<Vec<CoinType>> {
        let (excluded_utxos, excluded_message_nonces) =
            split_into_utxo_ids_and_nonces(excluded_coins);

        let filter = ResourceFilter {
            from: self.address().clone(),
            asset_id: Some(asset_id),
            amount,
            excluded_utxos,
            excluded_message_nonces,
        };

        self.try_provider()?.get_spendable_resources(filter).await
    }

    /// Returns a vector containing the output coin and change output given an asset and amount
    fn get_asset_outputs_for_amount(
        &self,
        to: &Bech32Address,
        asset_id: AssetId,
        amount: u64,
    ) -> Vec<Output> {
        vec![
            Output::coin(to.into(), amount, asset_id),
            // Note that the change will be computed by the node.
            // Here we only have to tell the node who will own the change and its asset ID.
            Output::change(self.address().into(), 0, asset_id),
        ]
    }

    /// Returns a vector consisting of `Input::Coin`s and `Input::Message`s for the given
    /// asset ID and amount.
    async fn get_asset_inputs_for_amount(
        &self,
        asset_id: AssetId,
        amount: u64,
        excluded_coins: Option<Vec<CoinTypeId>>,
    ) -> Result<Vec<Input>>;

    /// Add base asset inputs to the transaction to cover the estimated fee
    /// and add a change output for the base asset if needed.
    /// Requires contract inputs to be at the start of the transactions inputs vec
    /// so that their indexes are retained
    async fn adjust_for_fee<Tb: TransactionBuilder + Sync>(
        &self,
        tb: &mut Tb,
        used_base_amount: u64,
    ) -> Result<()> {
        let provider = self.try_provider()?;
        let consensus_parameters = provider.consensus_parameters().await?;
        let (base_assets, base_amount) =
            available_base_assets_and_amount(tb, consensus_parameters.base_asset_id());
        let missing_base_amount =
            calculate_missing_base_amount(tb, base_amount, used_base_amount, provider).await?;

        if missing_base_amount > 0 {
            let new_base_inputs = self
                .get_asset_inputs_for_amount(
                    *consensus_parameters.base_asset_id(),
                    missing_base_amount,
                    Some(base_assets),
                )
                .await
                // if there query fails do nothing
                .unwrap_or_default();

            tb.inputs_mut().extend(new_base_inputs);
        };

        add_base_change_if_needed(tb, self.address(), consensus_parameters.base_asset_id());

        Ok(())
    }
}

#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait Account: ViewOnlyAccount {
    // Add signatures to the builder if the underlying account is a wallet
    fn add_witnesses<Tb: TransactionBuilder>(&self, _tb: &mut Tb) -> Result<()> {
        Ok(())
    }

    /// Transfer funds from this account to another `Address`.
    /// Fails if amount for asset ID is larger than address's spendable coins.
    /// Returns the transaction ID that was sent and the list of receipts.
    async fn transfer(
        &self,
        to: &Bech32Address,
        amount: u64,
        asset_id: AssetId,
        tx_policies: TxPolicies,
    ) -> Result<(TxId, Vec<Receipt>)> {
        let provider = self.try_provider()?;

        let inputs = self
            .get_asset_inputs_for_amount(asset_id, amount, None)
            .await?;
        let outputs = self.get_asset_outputs_for_amount(to, asset_id, amount);

        let mut tx_builder =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, tx_policies);

        self.add_witnesses(&mut tx_builder)?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let used_base_amount = if asset_id == *consensus_parameters.base_asset_id() {
            amount
        } else {
            0
        };
        self.adjust_for_fee(&mut tx_builder, used_base_amount)
            .await?;

        let tx = tx_builder.build(provider).await?;
        let tx_id = tx.id(consensus_parameters.chain_id());

        let tx_status = provider.send_transaction_and_await_commit(tx).await?;

        let receipts = tx_status.take_receipts_checked(None)?;

        Ok((tx_id, receipts))
    }

    /// Unconditionally transfers `balance` of type `asset_id` to
    /// the contract at `to`.
    /// Fails if balance for `asset_id` is larger than this account's spendable balance.
    /// Returns the corresponding transaction ID and the list of receipts.
    ///
    /// CAUTION !!!
    ///
    /// This will transfer coins to a contract, possibly leading
    /// to the PERMANENT LOSS OF COINS if not used with care.
    async fn force_transfer_to_contract(
        &self,
        to: &Bech32ContractId,
        balance: u64,
        asset_id: AssetId,
        tx_policies: TxPolicies,
    ) -> Result<(String, Vec<Receipt>)> {
        let provider = self.try_provider()?;

        let zeroes = Bytes32::zeroed();
        let plain_contract_id: ContractId = to.into();

        let mut inputs = vec![Input::contract(
            UtxoId::new(zeroes, 0),
            zeroes,
            zeroes,
            TxPointer::default(),
            plain_contract_id,
        )];

        inputs.extend(
            self.get_asset_inputs_for_amount(asset_id, balance, None)
                .await?,
        );

        let outputs = vec![
            Output::contract(0, zeroes, zeroes),
            Output::change(self.address().into(), 0, asset_id),
        ];

        // Build transaction and sign it
        let mut tb = ScriptTransactionBuilder::prepare_contract_transfer(
            plain_contract_id,
            balance,
            asset_id,
            inputs,
            outputs,
            tx_policies,
        );

        self.add_witnesses(&mut tb)?;
        self.adjust_for_fee(&mut tb, balance).await?;

        let tx = tb.build(provider).await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let tx_id = tx.id(consensus_parameters.chain_id());
        let tx_status = provider.send_transaction_and_await_commit(tx).await?;

        let receipts = tx_status.take_receipts_checked(None)?;

        Ok((tx_id.to_string(), receipts))
    }

    /// Withdraws an amount of the base asset to
    /// an address on the base chain.
    /// Returns the transaction ID, message ID and the list of receipts.
    async fn withdraw_to_base_layer(
        &self,
        to: &Bech32Address,
        amount: u64,
        tx_policies: TxPolicies,
    ) -> Result<(TxId, Nonce, Vec<Receipt>)> {
        let provider = self.try_provider()?;
        let consensus_parameters = provider.consensus_parameters().await?;

        let inputs = self
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), amount, None)
            .await?;

        let mut tb = ScriptTransactionBuilder::prepare_message_to_output(
            to.into(),
            amount,
            inputs,
            tx_policies,
            *consensus_parameters.base_asset_id(),
        );

        self.add_witnesses(&mut tb)?;
        self.adjust_for_fee(&mut tb, amount).await?;

        let tx = tb.build(provider).await?;

        let tx_id = tx.id(consensus_parameters.chain_id());
        let tx_status = provider.send_transaction_and_await_commit(tx).await?;

        let receipts = tx_status.take_receipts_checked(None)?;

        let nonce = extract_message_nonce(&receipts)
            .expect("MessageId could not be retrieved from tx receipts.");

        Ok((tx_id, nonce, receipts))
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuel_crypto::{Message, SecretKey, Signature};
    use fuel_tx::{Address, ConsensusParameters, Output, Transaction as FuelTransaction};
    use fuels_core::{
        traits::Signer,
        types::{transaction::Transaction, DryRun, DryRunner},
    };
    use rand::{rngs::StdRng, RngCore, SeedableRng};

    use super::*;
    use crate::wallet::WalletUnlocked;

    #[tokio::test]
    async fn sign_and_verify() -> Result<()> {
        // ANCHOR: sign_message
        let mut rng = StdRng::seed_from_u64(2322u64);
        let mut secret_seed = [0u8; 32];
        rng.fill_bytes(&mut secret_seed);

        let secret = secret_seed.as_slice().try_into()?;

        // Create a wallet using the private key created above.
        let wallet = WalletUnlocked::new_from_private_key(secret, None);

        let message = Message::new("my message".as_bytes());
        let signature = wallet.sign(message).await?;

        // Check if signature is what we expect it to be
        assert_eq!(signature, Signature::from_str("0x8eeb238db1adea4152644f1cd827b552dfa9ab3f4939718bb45ca476d167c6512a656f4d4c7356bfb9561b14448c230c6e7e4bd781df5ee9e5999faa6495163d")?);

        // Recover address that signed the message
        let recovered_address = signature.recover(&message)?;

        assert_eq!(wallet.address().hash(), recovered_address.hash());

        // Verify signature
        signature.verify(&recovered_address, &message)?;
        // ANCHOR_END: sign_message

        Ok(())
    }

    #[derive(Default)]
    struct MockDryRunner {
        c_param: ConsensusParameters,
    }

    #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
    impl DryRunner for MockDryRunner {
        async fn dry_run(&self, _: FuelTransaction) -> Result<DryRun> {
            Ok(DryRun {
                succeeded: true,
                script_gas: 0,
                variable_outputs: 0,
            })
        }

        async fn consensus_parameters(&self) -> Result<ConsensusParameters> {
            Ok(self.c_param.clone())
        }

        async fn estimate_gas_price(&self, _block_header: u32) -> Result<u64> {
            Ok(0)
        }

        async fn estimate_predicates(
            &self,
            _: &FuelTransaction,
            _: Option<u32>,
        ) -> Result<FuelTransaction> {
            unimplemented!()
        }
    }

    #[tokio::test]
    async fn sign_tx_and_verify() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: sign_tb
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;
        let wallet = WalletUnlocked::new_from_private_key(secret, None);

        // Set up a transaction
        let mut tb = {
            let input_coin = Input::ResourceSigned {
                resource: CoinType::Coin(Coin {
                    amount: 10000000,
                    owner: wallet.address().clone(),
                    ..Default::default()
                }),
            };

            let output_coin = Output::coin(
                Address::from_str(
                    "0xc7862855b418ba8f58878db434b21053a61a2025209889cc115989e8040ff077",
                )?,
                1,
                Default::default(),
            );
            let change = Output::change(wallet.address().into(), 0, Default::default());

            ScriptTransactionBuilder::prepare_transfer(
                vec![input_coin],
                vec![output_coin, change],
                Default::default(),
            )
        };

        // Add `Signer` to the transaction builder
        tb.add_signer(wallet.clone())?;
        // ANCHOR_END: sign_tb

        let tx = tb.build(MockDryRunner::default()).await?; // Resolve signatures and add corresponding witness indexes

        // Extract the signature from the tx witnesses
        let bytes = <[u8; Signature::LEN]>::try_from(tx.witnesses().first().unwrap().as_ref())?;
        let tx_signature = Signature::from_bytes(bytes);

        // Sign the transaction manually
        let message = Message::from_bytes(*tx.id(0.into()));
        let signature = wallet.sign(message).await?;

        // Check if the signatures are the same
        assert_eq!(signature, tx_signature);

        // Check if the signature is what we expect it to be
        assert_eq!(signature, Signature::from_str("faa616776a1c336ef6257f7cb0cb5cd932180e2d15faba5f17481dae1cbcaf314d94617bd900216a6680bccb1ea62438e4ca93b0d5733d33788ef9d79cc24e9f")?);

        // Recover the address that signed the transaction
        let recovered_address = signature.recover(&message)?;

        assert_eq!(wallet.address().hash(), recovered_address.hash());

        // Verify signature
        signature.verify(&recovered_address, &message)?;

        Ok(())
    }
}\n```

Adding Signers to a transaction builder

Every signed resource in the inputs needs to have a witness index that points to a valid witness. Changing the witness index inside an input will change the transaction ID. This means that we need to set all witness indexes before finally signing the transaction. Previously, the user had to make sure that the witness indexes and the order of the witnesses are correct. To automate this process, the SDK will keep track of the signers in the transaction builder and resolve the final transaction automatically. This is done by storing signers until the final transaction is built.

Below is a full example of how to create a transaction builder and add signers to it.

Note: When you add a Signer to a transaction builder, the signer is stored inside it and the transaction will not be resolved until you call build()!

```rust\nuse std::collections::HashMap;

use async_trait::async_trait;
use fuel_core_client::client::pagination::{PaginatedResult, PaginationRequest};
use fuel_tx::{Output, Receipt, TxId, TxPointer, UtxoId};
use fuel_types::{AssetId, Bytes32, ContractId, Nonce};
use fuels_core::types::{
    bech32::{Bech32Address, Bech32ContractId},
    coin::Coin,
    coin_type::CoinType,
    coin_type_id::CoinTypeId,
    errors::Result,
    input::Input,
    message::Message,
    transaction::{Transaction, TxPolicies},
    transaction_builders::{BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder},
    transaction_response::TransactionResponse,
};

use crate::{
    accounts_utils::{
        add_base_change_if_needed, available_base_assets_and_amount, calculate_missing_base_amount,
        extract_message_nonce, split_into_utxo_ids_and_nonces,
    },
    provider::{Provider, ResourceFilter},
};

#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait ViewOnlyAccount: std::fmt::Debug + Send + Sync + Clone {
    fn address(&self) -> &Bech32Address;

    fn try_provider(&self) -> Result<&Provider>;

    async fn get_transactions(
        &self,
        request: PaginationRequest<String>,
    ) -> Result<PaginatedResult<TransactionResponse, String>> {
        Ok(self
            .try_provider()?
            .get_transactions_by_owner(self.address(), request)
            .await?)
    }

    /// Gets all unspent coins of asset `asset_id` owned by the account.
    async fn get_coins(&self, asset_id: AssetId) -> Result<Vec<Coin>> {
        Ok(self
            .try_provider()?
            .get_coins(self.address(), asset_id)
            .await?)
    }

    /// Get the balance of all spendable coins `asset_id` for address `address`. This is different
    /// from getting coins because we are just returning a number (the sum of UTXOs amount) instead
    /// of the UTXOs.
    async fn get_asset_balance(&self, asset_id: &AssetId) -> Result<u64> {
        self.try_provider()?
            .get_asset_balance(self.address(), *asset_id)
            .await
    }

    /// Gets all unspent messages owned by the account.
    async fn get_messages(&self) -> Result<Vec<Message>> {
        Ok(self.try_provider()?.get_messages(self.address()).await?)
    }

    /// Get all the spendable balances of all assets for the account. This is different from getting
    /// the coins because we are only returning the sum of UTXOs coins amount and not the UTXOs
    /// coins themselves.
    async fn get_balances(&self) -> Result<HashMap<String, u128>> {
        self.try_provider()?.get_balances(self.address()).await
    }

    /// Get some spendable resources (coins and messages) of asset `asset_id` owned by the account
    /// that add up at least to amount `amount`. The returned coins (UTXOs) are actual coins that
    /// can be spent. The number of UXTOs is optimized to prevent dust accumulation.
    async fn get_spendable_resources(
        &self,
        asset_id: AssetId,
        amount: u64,
        excluded_coins: Option<Vec<CoinTypeId>>,
    ) -> Result<Vec<CoinType>> {
        let (excluded_utxos, excluded_message_nonces) =
            split_into_utxo_ids_and_nonces(excluded_coins);

        let filter = ResourceFilter {
            from: self.address().clone(),
            asset_id: Some(asset_id),
            amount,
            excluded_utxos,
            excluded_message_nonces,
        };

        self.try_provider()?.get_spendable_resources(filter).await
    }

    /// Returns a vector containing the output coin and change output given an asset and amount
    fn get_asset_outputs_for_amount(
        &self,
        to: &Bech32Address,
        asset_id: AssetId,
        amount: u64,
    ) -> Vec<Output> {
        vec![
            Output::coin(to.into(), amount, asset_id),
            // Note that the change will be computed by the node.
            // Here we only have to tell the node who will own the change and its asset ID.
            Output::change(self.address().into(), 0, asset_id),
        ]
    }

    /// Returns a vector consisting of `Input::Coin`s and `Input::Message`s for the given
    /// asset ID and amount.
    async fn get_asset_inputs_for_amount(
        &self,
        asset_id: AssetId,
        amount: u64,
        excluded_coins: Option<Vec<CoinTypeId>>,
    ) -> Result<Vec<Input>>;

    /// Add base asset inputs to the transaction to cover the estimated fee
    /// and add a change output for the base asset if needed.
    /// Requires contract inputs to be at the start of the transactions inputs vec
    /// so that their indexes are retained
    async fn adjust_for_fee<Tb: TransactionBuilder + Sync>(
        &self,
        tb: &mut Tb,
        used_base_amount: u64,
    ) -> Result<()> {
        let provider = self.try_provider()?;
        let consensus_parameters = provider.consensus_parameters().await?;
        let (base_assets, base_amount) =
            available_base_assets_and_amount(tb, consensus_parameters.base_asset_id());
        let missing_base_amount =
            calculate_missing_base_amount(tb, base_amount, used_base_amount, provider).await?;

        if missing_base_amount > 0 {
            let new_base_inputs = self
                .get_asset_inputs_for_amount(
                    *consensus_parameters.base_asset_id(),
                    missing_base_amount,
                    Some(base_assets),
                )
                .await
                // if there query fails do nothing
                .unwrap_or_default();

            tb.inputs_mut().extend(new_base_inputs);
        };

        add_base_change_if_needed(tb, self.address(), consensus_parameters.base_asset_id());

        Ok(())
    }
}

#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait Account: ViewOnlyAccount {
    // Add signatures to the builder if the underlying account is a wallet
    fn add_witnesses<Tb: TransactionBuilder>(&self, _tb: &mut Tb) -> Result<()> {
        Ok(())
    }

    /// Transfer funds from this account to another `Address`.
    /// Fails if amount for asset ID is larger than address's spendable coins.
    /// Returns the transaction ID that was sent and the list of receipts.
    async fn transfer(
        &self,
        to: &Bech32Address,
        amount: u64,
        asset_id: AssetId,
        tx_policies: TxPolicies,
    ) -> Result<(TxId, Vec<Receipt>)> {
        let provider = self.try_provider()?;

        let inputs = self
            .get_asset_inputs_for_amount(asset_id, amount, None)
            .await?;
        let outputs = self.get_asset_outputs_for_amount(to, asset_id, amount);

        let mut tx_builder =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, tx_policies);

        self.add_witnesses(&mut tx_builder)?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let used_base_amount = if asset_id == *consensus_parameters.base_asset_id() {
            amount
        } else {
            0
        };
        self.adjust_for_fee(&mut tx_builder, used_base_amount)
            .await?;

        let tx = tx_builder.build(provider).await?;
        let tx_id = tx.id(consensus_parameters.chain_id());

        let tx_status = provider.send_transaction_and_await_commit(tx).await?;

        let receipts = tx_status.take_receipts_checked(None)?;

        Ok((tx_id, receipts))
    }

    /// Unconditionally transfers `balance` of type `asset_id` to
    /// the contract at `to`.
    /// Fails if balance for `asset_id` is larger than this account's spendable balance.
    /// Returns the corresponding transaction ID and the list of receipts.
    ///
    /// CAUTION !!!
    ///
    /// This will transfer coins to a contract, possibly leading
    /// to the PERMANENT LOSS OF COINS if not used with care.
    async fn force_transfer_to_contract(
        &self,
        to: &Bech32ContractId,
        balance: u64,
        asset_id: AssetId,
        tx_policies: TxPolicies,
    ) -> Result<(String, Vec<Receipt>)> {
        let provider = self.try_provider()?;

        let zeroes = Bytes32::zeroed();
        let plain_contract_id: ContractId = to.into();

        let mut inputs = vec![Input::contract(
            UtxoId::new(zeroes, 0),
            zeroes,
            zeroes,
            TxPointer::default(),
            plain_contract_id,
        )];

        inputs.extend(
            self.get_asset_inputs_for_amount(asset_id, balance, None)
                .await?,
        );

        let outputs = vec![
            Output::contract(0, zeroes, zeroes),
            Output::change(self.address().into(), 0, asset_id),
        ];

        // Build transaction and sign it
        let mut tb = ScriptTransactionBuilder::prepare_contract_transfer(
            plain_contract_id,
            balance,
            asset_id,
            inputs,
            outputs,
            tx_policies,
        );

        self.add_witnesses(&mut tb)?;
        self.adjust_for_fee(&mut tb, balance).await?;

        let tx = tb.build(provider).await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let tx_id = tx.id(consensus_parameters.chain_id());
        let tx_status = provider.send_transaction_and_await_commit(tx).await?;

        let receipts = tx_status.take_receipts_checked(None)?;

        Ok((tx_id.to_string(), receipts))
    }

    /// Withdraws an amount of the base asset to
    /// an address on the base chain.
    /// Returns the transaction ID, message ID and the list of receipts.
    async fn withdraw_to_base_layer(
        &self,
        to: &Bech32Address,
        amount: u64,
        tx_policies: TxPolicies,
    ) -> Result<(TxId, Nonce, Vec<Receipt>)> {
        let provider = self.try_provider()?;
        let consensus_parameters = provider.consensus_parameters().await?;

        let inputs = self
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), amount, None)
            .await?;

        let mut tb = ScriptTransactionBuilder::prepare_message_to_output(
            to.into(),
            amount,
            inputs,
            tx_policies,
            *consensus_parameters.base_asset_id(),
        );

        self.add_witnesses(&mut tb)?;
        self.adjust_for_fee(&mut tb, amount).await?;

        let tx = tb.build(provider).await?;

        let tx_id = tx.id(consensus_parameters.chain_id());
        let tx_status = provider.send_transaction_and_await_commit(tx).await?;

        let receipts = tx_status.take_receipts_checked(None)?;

        let nonce = extract_message_nonce(&receipts)
            .expect("MessageId could not be retrieved from tx receipts.");

        Ok((tx_id, nonce, receipts))
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuel_crypto::{Message, SecretKey, Signature};
    use fuel_tx::{Address, ConsensusParameters, Output, Transaction as FuelTransaction};
    use fuels_core::{
        traits::Signer,
        types::{transaction::Transaction, DryRun, DryRunner},
    };
    use rand::{rngs::StdRng, RngCore, SeedableRng};

    use super::*;
    use crate::wallet::WalletUnlocked;

    #[tokio::test]
    async fn sign_and_verify() -> Result<()> {
        // ANCHOR: sign_message
        let mut rng = StdRng::seed_from_u64(2322u64);
        let mut secret_seed = [0u8; 32];
        rng.fill_bytes(&mut secret_seed);

        let secret = secret_seed.as_slice().try_into()?;

        // Create a wallet using the private key created above.
        let wallet = WalletUnlocked::new_from_private_key(secret, None);

        let message = Message::new("my message".as_bytes());
        let signature = wallet.sign(message).await?;

        // Check if signature is what we expect it to be
        assert_eq!(signature, Signature::from_str("0x8eeb238db1adea4152644f1cd827b552dfa9ab3f4939718bb45ca476d167c6512a656f4d4c7356bfb9561b14448c230c6e7e4bd781df5ee9e5999faa6495163d")?);

        // Recover address that signed the message
        let recovered_address = signature.recover(&message)?;

        assert_eq!(wallet.address().hash(), recovered_address.hash());

        // Verify signature
        signature.verify(&recovered_address, &message)?;
        // ANCHOR_END: sign_message

        Ok(())
    }

    #[derive(Default)]
    struct MockDryRunner {
        c_param: ConsensusParameters,
    }

    #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
    impl DryRunner for MockDryRunner {
        async fn dry_run(&self, _: FuelTransaction) -> Result<DryRun> {
            Ok(DryRun {
                succeeded: true,
                script_gas: 0,
                variable_outputs: 0,
            })
        }

        async fn consensus_parameters(&self) -> Result<ConsensusParameters> {
            Ok(self.c_param.clone())
        }

        async fn estimate_gas_price(&self, _block_header: u32) -> Result<u64> {
            Ok(0)
        }

        async fn estimate_predicates(
            &self,
            _: &FuelTransaction,
            _: Option<u32>,
        ) -> Result<FuelTransaction> {
            unimplemented!()
        }
    }

    #[tokio::test]
    async fn sign_tx_and_verify() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: sign_tb
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;
        let wallet = WalletUnlocked::new_from_private_key(secret, None);

        // Set up a transaction
        let mut tb = {
            let input_coin = Input::ResourceSigned {
                resource: CoinType::Coin(Coin {
                    amount: 10000000,
                    owner: wallet.address().clone(),
                    ..Default::default()
                }),
            };

            let output_coin = Output::coin(
                Address::from_str(
                    "0xc7862855b418ba8f58878db434b21053a61a2025209889cc115989e8040ff077",
                )?,
                1,
                Default::default(),
            );
            let change = Output::change(wallet.address().into(), 0, Default::default());

            ScriptTransactionBuilder::prepare_transfer(
                vec![input_coin],
                vec![output_coin, change],
                Default::default(),
            )
        };

        // Add `Signer` to the transaction builder
        tb.add_signer(wallet.clone())?;
        // ANCHOR_END: sign_tb

        let tx = tb.build(MockDryRunner::default()).await?; // Resolve signatures and add corresponding witness indexes

        // Extract the signature from the tx witnesses
        let bytes = <[u8; Signature::LEN]>::try_from(tx.witnesses().first().unwrap().as_ref())?;
        let tx_signature = Signature::from_bytes(bytes);

        // Sign the transaction manually
        let message = Message::from_bytes(*tx.id(0.into()));
        let signature = wallet.sign(message).await?;

        // Check if the signatures are the same
        assert_eq!(signature, tx_signature);

        // Check if the signature is what we expect it to be
        assert_eq!(signature, Signature::from_str("faa616776a1c336ef6257f7cb0cb5cd932180e2d15faba5f17481dae1cbcaf314d94617bd900216a6680bccb1ea62438e4ca93b0d5733d33788ef9d79cc24e9f")?);

        // Recover the address that signed the transaction
        let recovered_address = signature.recover(&message)?;

        assert_eq!(wallet.address().hash(), recovered_address.hash());

        // Verify signature
        signature.verify(&recovered_address, &message)?;

        Ok(())
    }
}\n```

Signing a built transaction

If you have a built transaction and want to add a signature, you can use the sign_with method.

```rust\nuse std::time::Duration;

use fuel_tx::{
    consensus_parameters::{ConsensusParametersV1, FeeParametersV1},
    ConsensusParameters, FeeParameters, Output,
};
use fuels::{
    core::codec::{calldata, encode_fn_selector, DecoderConfig, EncoderConfig},
    prelude::*,
    programs::DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
    tx::ContractParameters,
    types::{errors::transaction::Reason, input::Input, Bits256, Identity},
};
use tokio::time::Instant;

#[tokio::test]
async fn test_multiple_args() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Make sure we can call the contract with multiple arguments
    let contract_methods = contract_instance.methods();
    let response = contract_methods.get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    let t = MyType { x: 5, y: 6 };
    let response = contract_methods.get_alt(t.clone()).call().await?;
    assert_eq!(response.value, t);

    let response = contract_methods.get_single(5).call().await?;
    assert_eq!(response.value, 5);
    Ok(())
}

#[tokio::test]
async fn test_contract_calling_contract() -> Result<()> {
    // Tests a contract call that calls another contract (FooCaller calls FooContract underneath)
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "lib_contract_instance2",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();
    let lib_contract_id2 = lib_contract_instance2.contract_id();

    // Call the contract directly. It increments the given value.
    let response = lib_contract_instance.methods().increment(42).call().await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance
        .methods()
        .increment_from_contracts(lib_contract_id, lib_contract_id2, 42)
        // Note that the two lib_contract_instances have different types
        .with_contracts(&[&lib_contract_instance, &lib_contract_instance2])
        .call()
        .await?;

    assert_eq!(86, response.value);

    // ANCHOR: external_contract
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    assert_eq!(43, response.value);

    // ANCHOR: external_contract_ids
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contract_ids(&[lib_contract_id.clone()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    assert_eq!(43, response.value);
    Ok(())
}

#[tokio::test]
async fn test_reverting_transaction() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertContract",
            project = "e2e/sway/contracts/revert_transaction_error"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .make_transaction_fail(true)
        .call()
        .await;

    assert!(matches!(
        response,
        Err(Error::Transaction(Reason::Reverted { revert_id, .. })) if revert_id == 128
    ));

    Ok(())
}

#[tokio::test]
async fn test_multiple_read_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MultiReadContract",
            project = "e2e/sway/contracts/multiple_read_calls"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MultiReadContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    contract_methods.store(42).call().await?;

    // Use "simulate" because the methods don't actually
    // run a transaction, but just a dry-run
    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);

    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_multi_call_beginner() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let (val_1, val_2): (u64, u64) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_pro() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let my_type_1 = MyType { x: 1, y: 2 };
    let my_type_2 = MyType { x: 3, y: 4 };

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(5);
    let call_handler_2 = contract_methods.get_single(6);
    let call_handler_3 = contract_methods.get_alt(my_type_1.clone());
    let call_handler_4 = contract_methods.get_alt(my_type_2.clone());
    let call_handler_5 = contract_methods.get_array([7; 2]);
    let call_handler_6 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2)
        .add_call(call_handler_3)
        .add_call(call_handler_4)
        .add_call(call_handler_5)
        .add_call(call_handler_6);

    let (val_1, val_2, type_1, type_2, array_1, array_2): (
        u64,
        u64,
        MyType,
        MyType,
        [u64; 2],
        [u64; 2],
    ) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 5);
    assert_eq!(val_2, 6);
    assert_eq!(type_1, my_type_1);
    assert_eq!(type_2, my_type_2);
    assert_eq!(array_1, [7; 2]);
    assert_eq!(array_2, [42; 2]);

    Ok(())
}

#[tokio::test]
async fn test_contract_call_fee_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let gas_limit = 800;
    let tolerance = Some(0.2);
    let block_horizon = Some(1);
    let expected_gas_used = 960;
    let expected_metered_bytes_size = 824;

    let estimated_transaction_cost = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit))
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?;

    assert_eq!(estimated_transaction_cost.gas_used, expected_gas_used);
    assert_eq!(
        estimated_transaction_cost.metered_bytes_size,
        expected_metered_bytes_size
    );

    Ok(())
}

#[tokio::test]
async fn contract_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let estimated_gas_used = contract_methods
        .initialize_counter(42)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn mult_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.initialize_counter(42);
    let call_handler_2 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let tolerance = Some(0.0);
    let block_horizon = Some(1);
    let estimated_gas_used = multi_call_handler
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = multi_call_handler.call::<(u64, [u64; 2])>().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn contract_method_call_respects_maturity() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BlockHeightContract",
            project = "e2e/sway/contracts/transaction_block_height"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BlockHeightContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_w_maturity = |maturity| {
        contract_instance
            .methods()
            .calling_this_will_produce_a_block()
            .with_tx_policies(TxPolicies::default().with_maturity(maturity))
    };

    call_w_maturity(1).call().await.expect(
        "should have passed since we're calling with a maturity \
         that is less or equal to the current block height",
    );

    call_w_maturity(3).call().await.expect_err(
        "should have failed since we're calling with a maturity \
         that is greater than the current block height",
    );

    Ok(())
}

#[tokio::test]
async fn test_auth_msg_sender_from_sdk() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "AuthContract",
            project = "e2e/sway/contracts/auth_testing_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "AuthContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Contract returns true if `msg_sender()` matches `wallet.address()`.
    let response = contract_instance
        .methods()
        .check_msg_sender(wallet.address())
        .call()
        .await?;

    assert!(response.value);
    Ok(())
}

#[tokio::test]
async fn test_large_return_data() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/large_return_data"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let res = contract_methods.get_id().call().await?;

    assert_eq!(
        res.value.0,
        [
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ]
    );

    // One word-sized string
    let res = contract_methods.get_small_string().call().await?;
    assert_eq!(res.value, "gggggggg");

    // Two word-sized string
    let res = contract_methods.get_large_string().call().await?;
    assert_eq!(res.value, "ggggggggg");

    // Large struct will be bigger than a `WORD`.
    let res = contract_methods.get_large_struct().call().await?;
    assert_eq!(res.value.foo, 12);
    assert_eq!(res.value.bar, 42);

    // Array will be returned in `ReturnData`.
    let res = contract_methods.get_large_array().call().await?;
    assert_eq!(res.value, [1, 2]);

    let res = contract_methods.get_contract_id().call().await?;

    // First `value` is from `CallResponse`.
    // Second `value` is from the `ContractId` type.
    assert_eq!(
        res.value,
        ContractId::from([
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ])
    );
    Ok(())
}

#[tokio::test]
async fn can_handle_function_called_new() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().new().call().await?.value;

    assert_eq!(response, 12345);
    Ok(())
}

#[tokio::test]
async fn test_contract_setup_macro_deploy_with_salt() -> Result<()> {
    // ANCHOR: contract_setup_macro_multi
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_caller_id = contract_caller_instance.contract_id();

    let contract_caller_id2 = contract_caller_instance2.contract_id();

    // Because we deploy with salt, we can deploy the same contract multiple times
    assert_ne!(contract_caller_id, contract_caller_id2);

    // The first contract can be called because they were deployed on the same provider
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance2
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);
    // ANCHOR_END: contract_setup_macro_multi

    Ok(())
}

#[tokio::test]
async fn test_wallet_getter() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(contract_instance.account().address(), wallet.address());
    //`contract_id()` is tested in
    // async fn test_contract_calling_contract() -> Result<()> {
    Ok(())
}

#[tokio::test]
async fn test_connect_wallet() -> Result<()> {
    // ANCHOR: contract_setup_macro_manual_wallet
    let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));

    let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
    let wallet = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    // ANCHOR_END: contract_setup_macro_manual_wallet

    // pay for call with wallet
    let tx_policies = TxPolicies::default()
        .with_tip(100)
        .with_script_gas_limit(1_000_000);

    contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm that funds have been deducted
    let wallet_balance = wallet.get_asset_balance(&Default::default()).await?;
    assert!(DEFAULT_COIN_AMOUNT > wallet_balance);

    // pay for call with wallet_2
    contract_instance
        .with_account(wallet_2.clone())
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm there are no changes to wallet, wallet_2 has been charged
    let wallet_balance_second_call = wallet.get_asset_balance(&Default::default()).await?;
    let wallet_2_balance = wallet_2.get_asset_balance(&Default::default()).await?;
    assert_eq!(wallet_balance_second_call, wallet_balance);
    assert!(DEFAULT_COIN_AMOUNT > wallet_2_balance);

    Ok(())
}

async fn setup_output_variable_estimation_test() -> Result<(
    Vec<WalletUnlocked>,
    [Identity; 3],
    AssetId,
    Bech32ContractId,
)> {
    let wallet_config = WalletsConfig::new(Some(3), None, None);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;

    let contract_id = Contract::load_from(
        "sway/contracts/token_ops/out/release/token_ops.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallets[0], TxPolicies::default())
    .await?;

    let mint_asset_id = contract_id.asset_id(&Bits256::zeroed());
    let addresses = wallets
        .iter()
        .map(|wallet| wallet.address().into())
        .collect::<Vec<_>>()
        .try_into()
        .unwrap();

    Ok((wallets, addresses, mint_asset_id, contract_id))
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());
    let contract_methods = contract_instance.methods();
    let amount = 1000;

    {
        // Should fail due to lack of output variables
        let response = contract_methods
            .mint_to_addresses(amount, addresses)
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    {
        // Should add 3 output variables automatically
        let _ = contract_methods
            .mint_to_addresses(amount, addresses)
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .call()
            .await?;

        for wallet in wallets.iter() {
            let balance = wallet.get_asset_balance(&mint_asset_id).await?;
            assert_eq!(balance, amount);
        }
    }

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallets[0].clone());
    let contract_methods = contract_instance.methods();
    const NUM_OF_CALLS: u64 = 3;
    let amount = 1000;
    let total_amount = amount * NUM_OF_CALLS;

    let mut multi_call_handler = CallHandler::new_multi_call(wallets[0].clone());
    for _ in 0..NUM_OF_CALLS {
        let call_handler = contract_methods.mint_to_addresses(amount, addresses);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    wallets[0]
        .force_transfer_to_contract(
            &contract_id,
            total_amount,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await
        .unwrap();

    let base_layer_address = Bits256([1u8; 32]);
    let call_handler = contract_methods.send_message(base_layer_address, amount);
    multi_call_handler = multi_call_handler.add_call(call_handler);

    let _ = multi_call_handler
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call::<((), (), ())>()
        .await?;

    for wallet in wallets.iter() {
        let balance = wallet.get_asset_balance(&mint_asset_id).await?;
        assert_eq!(balance, 3 * amount);
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_instance_get_balances() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let (coins, asset_ids) = setup_multiple_assets_coins(wallet.address(), 2, 4, 8);

    let random_asset_id = &asset_ids[1];
    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_id = contract_instance.contract_id();

    // Check the current balance of the contract with id 'contract_id'
    let contract_balances = contract_instance.get_balances().await?;
    assert!(contract_balances.is_empty());

    // Transfer an amount to the contract
    let amount = 8;
    wallet
        .force_transfer_to_contract(contract_id, amount, *random_asset_id, TxPolicies::default())
        .await?;

    // Check that the contract now has 1 coin
    let contract_balances = contract_instance.get_balances().await?;
    assert_eq!(contract_balances.len(), 1);

    let random_asset_balance = contract_balances.get(random_asset_id).unwrap();
    assert_eq!(*random_asset_balance, amount);

    Ok(())
}

#[tokio::test]
async fn contract_call_futures_implement_send() -> Result<()> {
    use std::future::Future;

    fn tokio_spawn_imitation<T>(_: T)
    where
        T: Future + Send + 'static,
    {
    }

    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    tokio_spawn_imitation(async move {
        contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await
            .unwrap();
    });
    Ok(())
}

#[tokio::test]
async fn test_contract_set_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let res = lib_contract_instance.methods().increment(42).call().await?;
    assert_eq!(43, res.value);

    {
        // Should fail due to missing external contracts
        let res = contract_caller_instance
            .methods()
            .increment_from_contract(lib_contract_id, 42)
            .call()
            .await;

        assert!(matches!(
            res,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    let res = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    assert_eq!(43, res.value);
    Ok(())
}

#[tokio::test]
async fn test_output_variable_contract_id_estimation_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_test_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_methods = contract_caller_instance.methods();

    let mut multi_call_handler =
        CallHandler::new_multi_call(wallet.clone()).with_tx_policies(Default::default());

    for _ in 0..3 {
        let call_handler = contract_methods.increment_from_contract(lib_contract_id, 42);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    // add call that does not need ContractId
    let contract_methods = contract_test_instance.methods();
    let call_handler = contract_methods.get(5, 6);

    multi_call_handler = multi_call_handler.add_call(call_handler);

    let call_response = multi_call_handler
        .determine_missing_contracts(None)
        .await?
        .call::<(u64, u64, u64, u64)>()
        .await?;

    assert_eq!(call_response.value, (43, 43, 43, 11));

    Ok(())
}

#[tokio::test]
async fn test_contract_call_with_non_default_max_input() -> Result<()> {
    use fuels::{
        tx::{ConsensusParameters, TxParameters},
        types::coin::Coin,
    };

    let mut consensus_parameters = ConsensusParameters::default();
    let tx_params = TxParameters::default()
        .with_max_inputs(123)
        .with_max_size(1_000_000);
    consensus_parameters.set_tx_params(tx_params);
    let contract_params = ContractParameters::default().with_contract_max_size(1_000_000);
    consensus_parameters.set_contract_params(contract_params);

    let mut wallet = WalletUnlocked::new_random(None);

    let coins: Vec<Coin> = setup_single_asset_coins(
        wallet.address(),
        Default::default(),
        DEFAULT_NUM_COINS,
        DEFAULT_COIN_AMOUNT,
    );
    let chain_config = ChainConfig {
        consensus_parameters: consensus_parameters.clone(),
        ..ChainConfig::default()
    };

    let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
    wallet.set_provider(provider.clone());
    assert_eq!(consensus_parameters, provider.consensus_parameters().await?);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn test_add_custom_assets() -> Result<()> {
    let initial_amount = 100_000;
    let asset_base = AssetConfig {
        id: AssetId::zeroed(),
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_1 = AssetId::from([3u8; 32]);
    let asset_1 = AssetConfig {
        id: asset_id_1,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_2 = AssetId::from([1u8; 32]);
    let asset_2 = AssetConfig {
        id: asset_id_2,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let assets = vec![asset_base, asset_1, asset_2];

    let num_wallets = 2;
    let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
    let mut wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
    let wallet_1 = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet_1",
            random_salt = false,
        ),
    );

    let amount_1 = 5000;
    let amount_2 = 3000;
    let response = contract_instance
        .methods()
        .get(5, 6)
        .add_custom_asset(asset_id_1, amount_1, Some(wallet_2.address().clone()))
        .add_custom_asset(asset_id_2, amount_2, Some(wallet_2.address().clone()))
        .call()
        .await?;

    assert_eq!(response.value, 11);

    let balance_asset_1 = wallet_1.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_1.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount - amount_1);
    assert_eq!(balance_asset_2, initial_amount - amount_2);

    let balance_asset_1 = wallet_2.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_2.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount + amount_1);
    assert_eq!(balance_asset_2, initial_amount + amount_2);

    Ok(())
}

#[tokio::test]
async fn contract_load_error_messages() {
    {
        let binary_path = "sway/contracts/contract_test/out/release/no_file_on_path.bin";
        let expected_error = format!("io: file \"{binary_path}\" does not exist");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
    {
        let binary_path = "sway/contracts/contract_test/out/release/contract_test-abi.json";
        let expected_error = format!("expected \"{binary_path}\" to have '.bin' extension");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
}

#[tokio::test]
async fn test_payable_annotation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/payable_annotation"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .payable()
        .call_params(
            CallParameters::default()
                .with_amount(100)
                .with_gas_forwarded(20_000),
        )?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    // ANCHOR: non_payable_params
    let err = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_amount(100))
        .expect_err("should return error");

    assert!(matches!(err, Error::Other(s) if s.contains("assets forwarded to non-payable method")));
    // ANCHOR_END: non_payable_params

    let response = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_gas_forwarded(20_000))?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    Ok(())
}

#[tokio::test]
async fn multi_call_from_calls_with_different_account_types() -> Result<()> {
    use fuels::prelude::*;

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallet = WalletUnlocked::new_random(None);
    let predicate = Predicate::from_code(vec![]);

    let contract_methods_wallet =
        MyContract::new(Bech32ContractId::default(), wallet.clone()).methods();
    let contract_methods_predicate =
        MyContract::new(Bech32ContractId::default(), predicate).methods();

    let call_handler_1 = contract_methods_wallet.initialize_counter(42);
    let call_handler_2 = contract_methods_predicate.get_array([42; 2]);

    let _multi_call_handler = CallHandler::new_multi_call(wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    Ok(())
}

#[tokio::test]
async fn low_level_call() -> Result<()> {
    use fuels::types::SizedAsciiString;

    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyCallerContract",
                project = "e2e/sway/contracts/low_level_caller"
            ),
            Contract(
                name = "MyTargetContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "caller_contract_instance",
            contract = "MyCallerContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "target_contract_instance",
            contract = "MyTargetContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let function_selector = encode_fn_selector("initialize_counter");
    let call_data = calldata!(42u64)?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let response = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await?;
    assert_eq!(response.value, 42);

    let function_selector = encode_fn_selector("set_value_multiple_complex");
    let call_data = calldata!(
        MyStruct {
            a: true,
            b: [1, 2, 3],
        },
        SizedAsciiString::<4>::try_from("fuel")?
    )?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let result_uint = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await
        .unwrap()
        .value;

    let result_bool = target_contract_instance
        .methods()
        .get_bool_value()
        .call()
        .await
        .unwrap()
        .value;

    let result_str = target_contract_instance
        .methods()
        .get_str_value()
        .call()
        .await
        .unwrap()
        .value;

    assert_eq!(result_uint, 42);
    assert!(result_bool);
    assert_eq!(result_str, "fuel");

    Ok(())
}

#[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
#[test]
fn db_rocksdb() {
    use std::{fs, str::FromStr};

    use fuels::{
        accounts::wallet::WalletUnlocked,
        client::{PageDirection, PaginationRequest},
        crypto::SecretKey,
        prelude::{setup_test_provider, DbType, Error, ViewOnlyAccount, DEFAULT_COIN_AMOUNT},
    };

    let temp_dir = tempfile::tempdir().expect("failed to make tempdir");
    let temp_dir_name = temp_dir
        .path()
        .file_name()
        .expect("failed to get file name")
        .to_string_lossy()
        .to_string();
    let temp_database_path = temp_dir.path().join("db");

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let _ = temp_dir;
            let wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            const NUMBER_OF_ASSETS: u64 = 2;
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let chain_config = ChainConfig {
                chain_name: temp_dir_name.clone(),
                consensus_parameters: Default::default(),
                ..ChainConfig::local_testnet()
            };

            let (coins, _) = setup_multiple_assets_coins(
                wallet.address(),
                NUMBER_OF_ASSETS,
                DEFAULT_NUM_COINS,
                DEFAULT_COIN_AMOUNT,
            );

            let provider =
                setup_test_provider(coins.clone(), vec![], Some(node_config), Some(chain_config))
                    .await?;

            provider.produce_blocks(2, None).await?;

            Ok::<(), Error>(())
        })
        .unwrap();

    // The runtime needs to be terminated because the node can currently only be killed when the runtime itself shuts down.

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let provider = setup_test_provider(vec![], vec![], Some(node_config), None).await?;
            // the same wallet that was used when rocksdb was built. When we connect it to the provider, we expect it to have the same amount of assets
            let mut wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            wallet.set_provider(provider.clone());

            let blocks = provider
                .get_blocks(PaginationRequest {
                    cursor: None,
                    results: 10,
                    direction: PageDirection::Forward,
                })
                .await?
                .results;

            assert_eq!(blocks.len(), 3);
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(wallet.get_balances().await?.len(), 2);

            fs::remove_dir_all(
                temp_database_path
                    .parent()
                    .expect("db parent folder does not exist"),
            )?;

            Ok::<(), Error>(())
        })
        .unwrap();
}

#[tokio::test]
async fn can_configure_decoding_of_contract_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: Will not work if max_tokens not big enough
        methods.i_return_a_1k_el_array().with_decoder_config(DecoderConfig{max_tokens: 100, ..Default::default()}).call().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Single call: Works when limit is bumped
        let result = methods
            .i_return_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(result, [0; 1000]);
    }
    {
        // Multi call: Will not work if max_tokens not big enough
        CallHandler::new_multi_call(wallet.clone())
         .add_call(methods.i_return_a_1k_el_array())
         .with_decoder_config(DecoderConfig { max_tokens: 100, ..Default::default() })
         .call::<([u8; 1000],)>().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Multi call: Works when configured
        CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_return_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<([u8; 1000],)>()
            .await
            .unwrap();
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let submitted_tx = contract_methods.get(1, 2).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;

    assert_eq!(value, 3);

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let handle = multi_call_handler.submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let (val_1, val_2): (u64, u64) = handle.response().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_heap_type_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vector_output"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        let call_handler_1 = contract_instance.methods().u8_in_vec(5);
        let call_handler_2 = contract_instance_2.methods().get_single(7);
        let call_handler_3 = contract_instance.methods().u8_in_vec(3);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2)
            .add_call(call_handler_3);

        let (val_1, val_2, val_3): (Vec<u8>, u64, Vec<u8>) = multi_call_handler.call().await?.value;

        assert_eq!(val_1, vec![0, 1, 2, 3, 4]);
        assert_eq!(val_2, 7);
        assert_eq!(val_3, vec![0, 1, 2]);
    }

    Ok(())
}

#[tokio::test]
async fn heap_types_correctly_offset_in_create_transactions_w_storage_slots() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Predicate(
            name = "MyPredicate",
            project = "e2e/sway/types/predicates/predicate_vector"
        ),),
    );

    let provider = wallet.try_provider()?.clone();
    let data = MyPredicateEncoder::default().encode_data(18, 24, vec![2, 4, 42])?;
    let predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(data)
    .with_provider(provider);

    wallet
        .transfer(
            predicate.address(),
            10_000,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await?;

    // if the contract is successfully deployed then the predicate was unlocked. This further means
    // the offsets were setup correctly since the predicate uses heap types in its arguments.
    // Storage slots were loaded automatically by default
    Contract::load_from(
        "sway/contracts/storage/out/release/storage.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    Ok(())
}

#[tokio::test]
async fn test_arguments_with_gas_forwarded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vectors"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let x = 128;
    let vec_input = vec![0, 1, 2];
    {
        let response = contract_instance
            .methods()
            .get_single(x)
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;

        assert_eq!(response.value, x);
    }
    {
        contract_instance_2
            .methods()
            .u32_vec(vec_input.clone())
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;
    }
    {
        let call_handler_1 = contract_instance.methods().get_single(x);
        let call_handler_2 = contract_instance_2.methods().u32_vec(vec_input);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let (value, _): (u64, ()) = multi_call_handler.call().await?.value;

        assert_eq!(value, x);
    }

    Ok(())
}

#[tokio::test]
async fn contract_custom_call_no_signatures_strategy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let provider = wallet.try_provider()?;

    let counter = 42;
    let call_handler = contract_instance.methods().initialize_counter(counter);

    let mut tb = call_handler.transaction_builder().await?;

    let base_asset_id = *provider.consensus_parameters().await?.base_asset_id();

    let amount = 10;
    let consensus_parameters = provider.consensus_parameters().await?;
    let new_base_inputs = wallet
        .get_asset_inputs_for_amount(base_asset_id, amount, None)
        .await?;
    tb.inputs_mut().extend(new_base_inputs);
    tb.outputs_mut()
        .push(Output::change(wallet.address().into(), 0, base_asset_id));

    // ANCHOR: tb_no_signatures_strategy
    let mut tx = tb
        .with_build_strategy(ScriptBuildStrategy::NoSignatures)
        .build(provider)
        .await?;
    // ANCHOR: tx_sign_with
    tx.sign_with(&wallet, consensus_parameters.chain_id())
        .await?;
    // ANCHOR_END: tx_sign_with
    // ANCHOR_END: tb_no_signatures_strategy

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;

    let tx_status = provider.tx_status(&tx_id).await?;

    let response = call_handler.get_response_from(tx_status)?;

    assert_eq!(counter, response.value);

    Ok(())
}

#[tokio::test]
async fn contract_encoder_config_is_applied() -> Result<()> {
    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Wallets("wallet")
    );
    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let instance = TestContract::new(contract_id.clone(), wallet.clone());

    {
        let _encoding_ok = instance
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let instance_with_encoder_config = instance.with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));

        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));
    }

    Ok(())
}

#[tokio::test]
async fn test_reentrant_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LibContractCaller",
            project = "e2e/sway/contracts/lib_contract_caller"
        ),),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = contract_caller_instance.contract_id();
    let response = contract_caller_instance
        .methods()
        .re_entrant(contract_id, true)
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn msg_sender_gas_estimation_issue() {
    // Gas estimation requires an input of the base asset. If absent, a fake input is
    // added. However, if a non-base coin is present and the fake input introduces a
    // second owner, it causes the `msg_sender` sway fn to fail. This leads
    // to a premature failure in gas estimation, risking transaction failure due to
    // a low gas limit.
    let mut wallet = WalletUnlocked::new_random(None);

    let (coins, ids) =
        setup_multiple_assets_coins(wallet.address(), 2, DEFAULT_NUM_COINS, DEFAULT_COIN_AMOUNT);

    let provider = setup_test_provider(coins, vec![], None, None)
        .await
        .unwrap();
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/msg_methods"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let asset_id = ids[0];

    // The fake coin won't be added if we add a base asset, so let's not do that
    assert!(
        asset_id
            != *provider
                .consensus_parameters()
                .await
                .unwrap()
                .base_asset_id()
    );
    let call_params = CallParameters::default()
        .with_amount(100)
        .with_asset_id(asset_id);

    contract_instance
        .methods()
        .message_sender()
        .call_params(call_params)
        .unwrap()
        .call()
        .await
        .unwrap();
}

#[tokio::test]
async fn variable_output_estimation_is_optimized() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/var_outputs"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_methods = contract_instance.methods();

    let coins = 252;
    let recipient = Identity::Address(wallet.address().into());
    let start = Instant::now();
    let _ = contract_methods
        .mint(coins, recipient)
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    // debug builds are slower (20x for `fuel-core-lib`, 4x for a release-fuel-core-binary)
    // we won't validate in that case so we don't have to maintain two expectations
    if !cfg!(debug_assertions) {
        let elapsed = start.elapsed().as_secs();
        let limit = 2;
        if elapsed > limit {
            panic!("Estimation took too long ({elapsed}). Limit is {limit}");
        }
    }

    Ok(())
}

async fn setup_node_with_high_price() -> Result<Vec<WalletUnlocked>> {
    let wallet_config = WalletsConfig::new(None, None, None);
    let fee_parameters = FeeParameters::V1(FeeParametersV1 {
        gas_price_factor: 92000,
        gas_per_byte: 63,
    });
    let consensus_parameters = ConsensusParameters::V1(ConsensusParametersV1 {
        fee_params: fee_parameters,
        ..Default::default()
    });
    let node_config = Some(NodeConfig {
        starting_gas_price: 1100,
        ..NodeConfig::default()
    });
    let chain_config = ChainConfig {
        consensus_parameters,
        ..ChainConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(wallet_config, node_config, Some(chain_config))
            .await?;

    Ok(wallets)
}

#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);

    let response = MyContract::new(contract_id, no_funds_wallet.clone())
        .methods()
        .get(5, 6)
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn simulations_can_be_made_without_coins_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let contract_instance = MyContract::new(contract_id, no_funds_wallet.clone());

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.get(1, 2);
    let call_handler_2 = contract_methods.get(3, 4);

    let mut multi_call_handler = CallHandler::new_multi_call(no_funds_wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let value: (u64, u64) = multi_call_handler
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value, (3, 7));

    Ok(())
}

#[tokio::test]
async fn contract_call_with_non_zero_base_asset_id_and_tip() -> Result<()> {
    use fuels::{prelude::*, tx::ConsensusParameters};

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let asset_id = AssetId::new([1; 32]);

    let mut consensus_parameters = ConsensusParameters::default();
    consensus_parameters.set_base_asset_id(asset_id);

    let config = ChainConfig {
        consensus_parameters,
        ..Default::default()
    };

    let asset_base = AssetConfig {
        id: asset_id,
        num_coins: 1,
        coin_amount: 10_000,
    };

    let wallet_config = WalletsConfig::new_multiple_assets(1, vec![asset_base]);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, Some(config)).await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_tip(10))
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn max_fee_estimation_respects_tolerance() -> Result<()> {
    use fuels::prelude::*;

    let mut call_wallet = WalletUnlocked::new_random(None);

    let call_coins = setup_single_asset_coins(call_wallet.address(), AssetId::BASE, 1000, 1);

    let mut deploy_wallet = WalletUnlocked::new_random(None);
    let deploy_coins =
        setup_single_asset_coins(deploy_wallet.address(), AssetId::BASE, 1, 1_000_000);

    let provider =
        setup_test_provider([call_coins, deploy_coins].concat(), vec![], None, None).await?;

    call_wallet.set_provider(provider.clone());
    deploy_wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            wallet = "deploy_wallet",
            contract = "MyContract",
            random_salt = false,
        )
    );
    let contract_instance = contract_instance.with_account(call_wallet.clone());

    let max_fee_from_tx = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            let builder = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap();

            assert_eq!(
                builder.max_fee_estimation_tolerance, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
                "Expected pre-set tolerance"
            );

            builder
                .with_max_fee_estimation_tolerance(tolerance)
                .build(&provider)
                .await
                .unwrap()
                .max_fee()
                .unwrap()
        }
    };

    let max_fee_from_builder = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance)
                .estimate_max_fee(&provider)
                .await
                .unwrap()
        }
    };

    let base_amount_in_inputs = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let call_wallet = &call_wallet;
        async move {
            let mut tb = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance);

            call_wallet.adjust_for_fee(&mut tb, 0).await.unwrap();
            tb.inputs
                .iter()
                .filter_map(|input: &Input| match input {
                    Input::ResourceSigned { resource }
                        if resource.coin_asset_id().unwrap() == AssetId::BASE =>
                    {
                        Some(resource.amount())
                    }
                    _ => None,
                })
                .sum::<u64>()
        }
    };

    let no_increase_max_fee = max_fee_from_tx(0.0).await;
    let increased_max_fee = max_fee_from_tx(2.00).await;

    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let no_increase_max_fee = max_fee_from_builder(0.0).await;
    let increased_max_fee = max_fee_from_builder(2.00).await;
    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let normal_base_asset = base_amount_in_inputs(0.0).await;
    let more_base_asset_due_to_bigger_tolerance = base_amount_in_inputs(5.00).await;
    assert!(more_base_asset_due_to_bigger_tolerance > normal_base_asset);

    Ok(())
}

#[tokio::test]
async fn blob_contract_deployment() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
    ));

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract_size = std::fs::metadata(contract_binary)
        .expect("contract file not found")
        .len();

    assert!(
         contract_size > 150_000,
         "the testnet size limit was around 100kB, we want a contract bigger than that to reflect prod (current: {contract_size}B)"
     );

    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::new(Some(2), None, None), None, None)
            .await?;

    let provider = wallets[0].provider().unwrap().clone();

    let consensus_parameters = provider.consensus_parameters().await?;

    let contract_max_size = consensus_parameters.contract_params().contract_max_size();
    assert!(
         contract_size > contract_max_size,
         "this test should ideally be run with a contract bigger than the max contract size ({contract_max_size}B) so that we know deployment couldn't have happened without blobs"
     );

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallets[0], TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn regular_contract_can_be_deployed() -> Result<()> {
    // given
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
    );

    let contract_binary = "sway/contracts/contract_test/out/release/contract_test.bin";

    // when
    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    // then
    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance
        .methods()
        .get_counter()
        .call()
        .await?
        .value;

    assert_eq!(response, 0);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_be_deployed_directly() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_upload_blobs_separately_then_deploy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    let blob_ids = contract.blob_ids();

    // if this were an example for the user we'd just call `deploy` on the contract above
    // this way we are testing that the blobs were really deployed above, otherwise the following
    // would fail
    let contract_id = Contract::loader_from_blob_ids(
        blob_ids.to_vec(),
        contract.salt(),
        contract.storage_slots().to_vec(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_blob_already_uploaded_not_an_issue() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?;

    // this will upload blobs
    contract
        .clone()
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    // this will try to upload the blobs but skip upon encountering an error
    let contract_id = contract
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .something()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_storage_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_storage_slots = contract.storage_slots().to_vec();

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";
    let proxy_contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let combined_storage_slots = [&contract_storage_slots, proxy_contract.storage_slots()].concat();

    let proxy_id = proxy_contract
        .with_storage_slots(combined_storage_slots)
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?
        .value;

    assert_eq!(response, 42);

    let _res = proxy
        .methods()
        .write_some_u64(36)
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 36);

    Ok(())
}\n```

Generating bindings with abigen

You might have noticed this snippet in the previous sections:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

The SDK lets you transform ABI methods of a smart contract, specified as JSON objects (which you can get from Forc), into Rust structs and methods that are type-checked at compile time. In order to call your contracts, scripts or predicates, you first need to generate the Rust bindings for them.

The following subsections contain more details about the abigen! syntax and the code generated from it.

The JSON ABI file

Whether you want to deploy or connect to a pre-existing smart contract, the JSON ABI file is extremely important: it's what tells the SDK about the ABI methods in your smart contracts.

For the same example Sway code as above:

contract;

abi MyContract {
    fn test_function() -> bool;
}

impl MyContract for Contract {
    fn test_function() -> bool {
        true
    }
}

The JSON ABI file looks like this:

$ cat out/release/my-test-abi.json
[
  {
    "type": "function",
    "inputs": [],
    "name": "test_function",
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "components": null
      }
    ]
  }
]

The Fuel Rust SDK will take this file as input and generate equivalent methods (and custom types if applicable) that you can call from your Rust code.

abigen

abigen! is a procedural macro -- it generates code. It accepts inputs in the format of:

ProgramType(name="MyProgramType", abi="my_program-abi.json")...

where:

  • ProgramType is one of: Contract, Script or Predicate,

  • name is the name that will be given to the generated bindings,

  • abi is either a path to the JSON ABI file or its actual contents.


So, an abigen! which generates bindings for two contracts and one script looks like this:

```rust\nextern crate alloc;

#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[test]
    fn example_of_abigen_usage() {
        // ANCHOR: multiple_abigen_program_types
        abigen!(
            Contract(
                name = "ContractA",
                abi = "e2e/sway/bindings/sharing_types/contract_a/out/release/contract_a-abi.json"
            ),
            Contract(
                name = "ContractB",
                abi = "e2e/sway/bindings/sharing_types/contract_b/out/release/contract_b-abi.json"
            ),
            Script(
                name = "MyScript",
                abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
            ),
            Predicate(
                name = "MyPredicateEncoder",
                abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
            ),
        );
        // ANCHOR_END: multiple_abigen_program_types
    }

    #[test]
    fn macro_deriving() {
        // ANCHOR: deriving_traits
        use fuels::macros::{Parameterize, Tokenizable};

        #[derive(Parameterize, Tokenizable)]
        struct MyStruct {
            field_a: u8,
        }

        #[derive(Parameterize, Tokenizable)]
        enum SomeEnum {
            A(MyStruct),
            B(Vec<u64>),
        }
        // ANCHOR_END: deriving_traits
    }
    #[test]
    fn macro_deriving_extra() {
        {
            use fuels::{
                core as fuels_core_elsewhere,
                macros::{Parameterize, Tokenizable},
                types as fuels_types_elsewhere,
            };

            // ANCHOR: deriving_traits_paths
            #[derive(Parameterize, Tokenizable)]
            #[FuelsCorePath = "fuels_core_elsewhere"]
            #[FuelsTypesPath = "fuels_types_elsewhere"]
            pub struct SomeStruct {
                field_a: u64,
            }
            // ANCHOR_END: deriving_traits_paths
        }
        {
            // ANCHOR: deriving_traits_nostd
            use fuels::macros::{Parameterize, Tokenizable};
            #[derive(Parameterize, Tokenizable)]
            #[NoStd]
            pub struct SomeStruct {
                field_a: u64,
            }
            // ANCHOR_END: deriving_traits_nostd
        }
    }
}\n```

How does the generated code look?

A rough overview:

pub mod abigen_bindings {
    pub mod contract_a_mod {
        struct SomeCustomStruct{/*...*/};
        // other custom types used in the contract

        struct ContractA {/*...*/};
        impl ContractA {/*...*/};
        // ...
    }
    pub mod contract_b_mod {
        // ...
    }
    pub mod my_script_mod {
        // ...
    }
    pub mod my_predicate_mod{
        // ...
    }
    pub mod shared_types{
        // ...
    }
}

pub use contract_a_mod::{/*..*/};
pub use contract_b_mod::{/*..*/};
pub use my_predicate_mod::{/*..*/};
pub use shared_types::{/*..*/};

Each ProgramType gets its own mod based on the name given in the abigen!. Inside the respective mods, the custom types used by that program are generated, and the bindings through which the actual calls can be made.

One extra mod called shared_types is generated if abigen! detects that the given programs share types. Instead of each mod regenerating the type for itself, the type is lifted out into the shared_types module, generated only once, and then shared between all program bindings that use it. Reexports are added to each mod so that even if a type is deemed shared, you can still access it as though each mod had generated the type for itself (i.e. my_contract_mod::SharedType).

A type is deemed shared if its name and definition match up. This can happen either because you've used the same library (a custom one or a type from the stdlib) or because you've happened to define the exact same type.

Finally, pub use statements are inserted, so you don't have to fully qualify the generated types. To avoid conflict, only types that have unique names will get a pub use statement. If you find rustc can't find your type, it might just be that there is another generated type with the same name. To fix the issue just qualify the path by doing abigen_bindings::whatever_contract_mod::TheType.

Note: It is highly encouraged that you generate all your bindings in one abigen! call. Doing it in this manner will allow type sharing and avoid name collisions you'd normally get when calling abigen! multiple times inside the same namespace. If you choose to proceed otherwise, keep in mind the generated code overview presented above and appropriately separate the abigen! calls into different modules to resolve the collision.

Using the bindings

Let's look at a contract with two methods: initialize_counter(arg: u64) -> u64 and increment_counter(arg: u64) -> u64, with the following JSON ABI:

```text\n{
  "programType": "contract",
  "specVersion": "1",
  "encodingVersion": "1",
  "concreteTypes": [
    {
      "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0",
      "type": "u64"
    }
  ],
  "functions": [
    {
      "inputs": [
        {
          "name": "value",
          "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
        }
      ],
      "name": "initialize_counter",
      "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
    },
    {
      "inputs": [
        {
          "name": "value",
          "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
        }
      ],
      "name": "increment_counter",
      "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
    }
  ],
  "metadataTypes": []
}\n```

By doing this:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::Result;

    #[tokio::test]
    #[allow(unused_variables)]
    async fn transform_json_to_bindings() -> Result<()> {
        use fuels::test_helpers::launch_provider_and_get_wallet;
        let wallet = launch_provider_and_get_wallet().await?;
        {
            // ANCHOR: use_abigen
            use fuels::prelude::*;
            // Replace with your own JSON abi path (relative to the root of your crate)
            abigen!(Contract(
                name = "MyContractName",
                abi = "examples/rust_bindings/src/abi.json"
            ));
            // ANCHOR_END: use_abigen
        }

        {
            // ANCHOR: abigen_with_string
            use fuels::prelude::*;
            abigen!(Contract(
                name = "MyContract",
                abi = r#"
            {
              "programType": "contract",
              "specVersion": "1",
              "encodingVersion": "1",
              "concreteTypes": [
                {
                  "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0",
                  "type": "u64"
                }
              ],
              "functions": [
                {
                  "inputs": [
                    {
                      "name": "value",
                      "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
                    }
                  ],
                  "name": "initialize_counter",
                  "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
                },
                {
                  "inputs": [
                    {
                      "name": "value",
                      "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
                    }
                  ],
                  "name": "increment_counter",
                  "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
                }
              ],
              "metadataTypes": []
            }
            "#
            ));
            // ANCHOR_END: abigen_with_string
        }
        Ok(())
    }
}\n```

or this:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::Result;

    #[tokio::test]
    #[allow(unused_variables)]
    async fn transform_json_to_bindings() -> Result<()> {
        use fuels::test_helpers::launch_provider_and_get_wallet;
        let wallet = launch_provider_and_get_wallet().await?;
        {
            // ANCHOR: use_abigen
            use fuels::prelude::*;
            // Replace with your own JSON abi path (relative to the root of your crate)
            abigen!(Contract(
                name = "MyContractName",
                abi = "examples/rust_bindings/src/abi.json"
            ));
            // ANCHOR_END: use_abigen
        }

        {
            // ANCHOR: abigen_with_string
            use fuels::prelude::*;
            abigen!(Contract(
                name = "MyContract",
                abi = r#"
            {
              "programType": "contract",
              "specVersion": "1",
              "encodingVersion": "1",
              "concreteTypes": [
                {
                  "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0",
                  "type": "u64"
                }
              ],
              "functions": [
                {
                  "inputs": [
                    {
                      "name": "value",
                      "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
                    }
                  ],
                  "name": "initialize_counter",
                  "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
                },
                {
                  "inputs": [
                    {
                      "name": "value",
                      "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
                    }
                  ],
                  "name": "increment_counter",
                  "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
                }
              ],
              "metadataTypes": []
            }
            "#
            ));
            // ANCHOR_END: abigen_with_string
        }
        Ok(())
    }
}\n```

you'll generate this (shortened for brevity's sake):

```rust\npub mod abigen_bindings {
    pub mod my_contract_mod {
        #[derive(Debug, Clone)]
        pub struct MyContract<A: ::fuels::accounts::Account> {
            contract_id: ::fuels::types::bech32::Bech32ContractId,
            account: A,
            log_decoder: ::fuels::core::codec::LogDecoder,
            encoder_config: ::fuels::core::codec::EncoderConfig,
        }
        impl<A: ::fuels::accounts::Account> MyContract<A> {
            pub fn new(
                contract_id: impl ::core::convert::Into<::fuels::types::bech32::Bech32ContractId>,
                account: A,
            ) -> Self {
                let contract_id: ::fuels::types::bech32::Bech32ContractId = contract_id.into();
                let log_decoder = ::fuels::core::codec::LogDecoder::new(
                    ::fuels::core::codec::log_formatters_lookup(vec![], contract_id.clone().into()),
                );
                let encoder_config = ::fuels::core::codec::EncoderConfig::default();
                Self {
                    contract_id,
                    account,
                    log_decoder,
                    encoder_config,
                }
            }
            pub fn contract_id(&self) -> &::fuels::types::bech32::Bech32ContractId {
                &self.contract_id
            }
            pub fn account(&self) -> A {
                self.account.clone()
            }
            pub fn with_account<U: ::fuels::accounts::Account>(self, account: U) -> MyContract<U> {
                MyContract {
                    contract_id: self.contract_id,
                    account,
                    log_decoder: self.log_decoder,
                    encoder_config: self.encoder_config,
                }
            }
            pub fn with_encoder_config(
                mut self,
                encoder_config: ::fuels::core::codec::EncoderConfig,
            ) -> MyContract<A> {
                self.encoder_config = encoder_config;
                self
            }
            pub async fn get_balances(
                &self,
            ) -> ::fuels::types::errors::Result<
                ::std::collections::HashMap<::fuels::types::AssetId, u64>,
            > {
                ::fuels::accounts::ViewOnlyAccount::try_provider(&self.account)?
                    .get_contract_balances(&self.contract_id)
                    .await
                    .map_err(::std::convert::Into::into)
            }
            pub fn methods(&self) -> MyContractMethods<A> {
                MyContractMethods {
                    contract_id: self.contract_id.clone(),
                    account: self.account.clone(),
                    log_decoder: self.log_decoder.clone(),
                    encoder_config: self.encoder_config.clone(),
                }
            }
        }
        pub struct MyContractMethods<A: ::fuels::accounts::Account> {
            contract_id: ::fuels::types::bech32::Bech32ContractId,
            account: A,
            log_decoder: ::fuels::core::codec::LogDecoder,
            encoder_config: ::fuels::core::codec::EncoderConfig,
        }
        impl<A: ::fuels::accounts::Account> MyContractMethods<A> {
            #[doc = " This method will read the counter from storage, increment it"]
            #[doc = " and write the incremented value to storage"]
            pub fn increment_counter(
                &self,
                value: ::core::primitive::u64,
            ) -> ::fuels::programs::calls::CallHandler<
                A,
                ::fuels::programs::calls::ContractCall,
                ::core::primitive::u64,
            > {
                ::fuels::programs::calls::CallHandler::new_contract_call(
                    self.contract_id.clone(),
                    self.account.clone(),
                    ::fuels::core::codec::encode_fn_selector("increment_counter"),
                    &[::fuels::core::traits::Tokenizable::into_token(value)],
                    self.log_decoder.clone(),
                    false,
                    self.encoder_config.clone(),
                )
            }
            pub fn initialize_counter(
                &self,
                value: ::core::primitive::u64,
            ) -> ::fuels::programs::calls::CallHandler<
                A,
                ::fuels::programs::calls::ContractCall,
                ::core::primitive::u64,
            > {
                ::fuels::programs::calls::CallHandler::new_contract_call(
                    self.contract_id.clone(),
                    self.account.clone(),
                    ::fuels::core::codec::encode_fn_selector("initialize_counter"),
                    &[::fuels::core::traits::Tokenizable::into_token(value)],
                    self.log_decoder.clone(),
                    false,
                    self.encoder_config.clone(),
                )
            }
        }
        impl<A: ::fuels::accounts::Account> ::fuels::programs::calls::ContractDependency for MyContract<A> {
            fn id(&self) -> ::fuels::types::bech32::Bech32ContractId {
                self.contract_id.clone()
            }
            fn log_decoder(&self) -> ::fuels::core::codec::LogDecoder {
                self.log_decoder.clone()
            }
        }
        #[derive(Clone, Debug, Default)]
        pub struct MyContractConfigurables {
            offsets_with_data: ::std::vec::Vec<(u64, ::std::vec::Vec<u8>)>,
            encoder: ::fuels::core::codec::ABIEncoder,
        }
        impl MyContractConfigurables {
            pub fn new(encoder_config: ::fuels::core::codec::EncoderConfig) -> Self {
                Self {
                    encoder: ::fuels::core::codec::ABIEncoder::new(encoder_config),
                    ..::std::default::Default::default()
                }
            }
        }
        impl From<MyContractConfigurables> for ::fuels::core::Configurables {
            fn from(config: MyContractConfigurables) -> Self {
                ::fuels::core::Configurables::new(config.offsets_with_data)
            }
        }
    }
}
pub use abigen_bindings::my_contract_mod::MyContract;
pub use abigen_bindings::my_contract_mod::MyContractConfigurables;
pub use abigen_bindings::my_contract_mod::MyContractMethods;\n```

Note: that is all generated code. No need to write any of that. Ever. The generated code might look different from one version to another, this is just an example to give you an idea of what it looks like.

Then, you're able to use it to call the actual methods on the deployed contract:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Deploying contracts

There are two main ways of working with contracts in the SDK: deploying a contract with SDK or using the SDK to interact with existing contracts.

Deploying a contract binary

Once you've written a contract in Sway and compiled it with forc build, you'll have in your hands two important artifacts: the compiled binary file and the JSON ABI file.

Note: Read here for more on how to work with Sway.

Below is how you can deploy your contracts using the SDK. For more details about each component in this process, read The abigen macro, The FuelVM binary file, and The JSON ABI file.

First, the Contract::load_from function is used to load a contract binary with a LoadConfiguration. If you are only interested in a single instance of your contract, use the default configuration: LoadConfiguration::default(). After the contract binary is loaded, you can use the deploy() method to deploy the contract to the blockchain.

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Alternatively, you can use LoadConfiguration to configure how the contract is loaded. LoadConfiguration let's you:

  • Load the same contract binary with Salt to get a new contract_id
  • Change the contract's storage slots
  • Update the contract's configurables

Note: The next section will give more information on how configurables can be used.

Additionally, you can set custom TxParameters when deploying the loaded contract.

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

After the contract is deployed, you can use the contract's methods like this:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Note: When redeploying an existing Contract, ensure that you initialize it with a unique salt to prevent deployment failures caused by a contract ID collision. To accomplish this, utilize the with_salt method to clone the existing Contract with a new salt.

Configurable constants

In Sway, you can define configurable constants which can be changed during the contract deployment in the SDK. Here is an example how the constants are defined.

```sway\ncontract;

#[allow(dead_code)]
enum EnumWithGeneric<D> {
    VariantOne: D,
    VariantTwo: (),
}

struct StructWithGeneric<D> {
    field_1: D,
    field_2: u64,
}

configurable {
    BOOL: bool = true,
    U8: u8 = 8,
    U16: u16 = 16,
    U32: u32 = 32,
    U64: u64 = 63,
    U256: u256 = 0x0000000000000000000000000000000000000000000000000000000000000008u256,
    B256: b256 = 0x0101010101010101010101010101010101010101010101010101010101010101,
    STR_4: str[4] = __to_str_array("fuel"),
    TUPLE: (u8, bool) = (8, true),
    ARRAY: [u32; 3] = [253, 254, 255],
    STRUCT: StructWithGeneric<u8> = StructWithGeneric {
        field_1: 8,
        field_2: 16,
    },
    ENUM: EnumWithGeneric<bool> = EnumWithGeneric::VariantOne(true),
}
//U128: u128 = 128, //TODO: add once https://github.com/FuelLabs/sway/issues/5356 is done

abi TestContract {
    fn return_configurables() -> (bool, u8, u16, u32, u64, u256, b256, str[4], (u8, bool), [u32; 3], StructWithGeneric<u8>, EnumWithGeneric<bool>);
}

impl TestContract for Contract {
    fn return_configurables() -> (bool, u8, u16, u32, u64, u256, b256, str[4], (u8, bool), [u32; 3], StructWithGeneric<u8>, EnumWithGeneric<bool>) {
        (BOOL, U8, U16, U32, U64, U256, B256, STR_4, TUPLE, ARRAY, STRUCT, ENUM)
    }
}\n```

Each of the configurable constants will get a dedicated with method in the SDK. For example, the constant STR_4 will get the with_STR_4 method which accepts the same type as defined in the contract code. Below is an example where we chain several with methods and deploy the contract with the new constants.

```rust\nuse fuels::{
    core::codec::EncoderConfig,
    prelude::*,
    types::{Bits256, SizedAsciiString, U256},
};

#[tokio::test]
async fn contract_default_configurables() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/configurables/out/release/configurables-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/contracts/configurables/out/release/configurables.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .return_configurables()
        .call()
        .await?;

    let expected_value = (
        true,
        8,
        16,
        32,
        63,
        U256::from(8),
        Bits256([1; 32]),
        "fuel".try_into()?,
        (8, true),
        [253, 254, 255],
        StructWithGeneric {
            field_1: 8u8,
            field_2: 16,
        },
        EnumWithGeneric::VariantOne(true),
    );

    assert_eq!(response.value, expected_value);

    Ok(())
}

#[tokio::test]
async fn script_default_configurables() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_configurables"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let mut script_instance = script_instance;
    script_instance.convert_into_loader().await?;

    let response = script_instance.main().call().await?;

    let expected_value = (
        true,
        8,
        16,
        32,
        63,
        U256::from(8),
        Bits256([1; 32]),
        "fuel".try_into()?,
        (8, true),
        [253, 254, 255],
        StructWithGeneric {
            field_1: 8u8,
            field_2: 16,
        },
        EnumWithGeneric::VariantOne(true),
    );

    assert_eq!(response.value, expected_value);

    Ok(())
}

#[tokio::test]
async fn contract_configurables() -> Result<()> {
    // ANCHOR: contract_configurables
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/configurables/out/release/configurables-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;

    let str_4: SizedAsciiString<4> = "FUEL".try_into()?;
    let new_struct = StructWithGeneric {
        field_1: 16u8,
        field_2: 32,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyContractConfigurables::default()
        .with_BOOL(false)?
        .with_U8(7)?
        .with_U16(15)?
        .with_U32(31)?
        .with_U64(63)?
        .with_U256(U256::from(8))?
        .with_B256(Bits256([2; 32]))?
        .with_STR_4(str_4.clone())?
        .with_TUPLE((7, false))?
        .with_ARRAY([252, 253, 254])?
        .with_STRUCT(new_struct.clone())?
        .with_ENUM(new_enum.clone())?;

    let contract_id = Contract::load_from(
        "sway/contracts/configurables/out/release/configurables.bin",
        LoadConfiguration::default().with_configurables(configurables),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());
    // ANCHOR_END: contract_configurables

    let response = contract_instance
        .methods()
        .return_configurables()
        .call()
        .await?;

    let expected_value = (
        false,
        7,
        15,
        31,
        63,
        U256::from(8),
        Bits256([2; 32]),
        str_4,
        (7, false),
        [252, 253, 254],
        new_struct,
        new_enum,
    );

    assert_eq!(response.value, expected_value);

    Ok(())
}

#[tokio::test]
async fn contract_manual_configurables() -> Result<()> {
    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/configurables"
        )),
        Wallets("wallet")
    );

    let str_4: SizedAsciiString<4> = "FUEL".try_into()?;
    let new_struct = StructWithGeneric {
        field_1: 16u8,
        field_2: 32,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyContractConfigurables::default()
        .with_BOOL(false)?
        .with_U8(7)?
        .with_U16(15)?
        .with_U32(31)?
        .with_U64(63)?
        .with_U256(U256::from(8))?
        .with_B256(Bits256([2; 32]))?
        .with_STR_4(str_4.clone())?
        .with_TUPLE((7, false))?
        .with_ARRAY([252, 253, 254])?
        .with_STRUCT(new_struct.clone())?
        .with_ENUM(new_enum.clone())?;

    let contract_id = Contract::load_from(
        "sway/contracts/configurables/out/release/configurables.bin",
        LoadConfiguration::default(),
    )?
    .with_configurables(configurables)
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .return_configurables()
        .call()
        .await?;

    let expected_value = (
        false,
        7,
        15,
        31,
        63,
        U256::from(8),
        Bits256([2; 32]),
        str_4,
        (7, false),
        [252, 253, 254],
        new_struct,
        new_enum,
    );

    assert_eq!(response.value, expected_value);

    Ok(())
}

#[tokio::test]
async fn script_configurables() -> Result<()> {
    // ANCHOR: script_configurables
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/script_configurables/out/release/script_configurables-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/scripts/script_configurables/out/release/script_configurables.bin";
    let instance = MyScript::new(wallet, bin_path);

    let str_4: SizedAsciiString<4> = "FUEL".try_into()?;
    let new_struct = StructWithGeneric {
        field_1: 16u8,
        field_2: 32,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyScriptConfigurables::new(EncoderConfig {
        max_tokens: 5,
        ..Default::default()
    })
    .with_BOOL(false)?
    .with_U8(7)?
    .with_U16(15)?
    .with_U32(31)?
    .with_U64(63)?
    .with_U256(U256::from(8))?
    .with_B256(Bits256([2; 32]))?
    .with_STR_4(str_4.clone())?
    .with_TUPLE((7, false))?
    .with_ARRAY([252, 253, 254])?
    .with_STRUCT(new_struct.clone())?
    .with_ENUM(new_enum.clone())?;

    let response = instance
        .with_configurables(configurables)
        .main()
        .call()
        .await?;
    // ANCHOR_END: script_configurables

    let expected_value = (
        false,
        7,
        15,
        31,
        63,
        U256::from(8),
        Bits256([2; 32]),
        str_4,
        (7, false),
        [252, 253, 254],
        new_struct,
        new_enum,
    );

    assert_eq!(response.value, expected_value);

    Ok(())
}

#[tokio::test]
async fn configurable_encoder_config_is_applied() {
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/script_configurables/out/release/script_configurables-abi.json"
    ));

    let new_struct = StructWithGeneric {
        field_1: 16u8,
        field_2: 32,
    };

    {
        let _configurables = MyScriptConfigurables::default()
            .with_STRUCT(new_struct.clone())
            .expect("no encoder config, it works");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };

        // Fails when a wrong encoder config is set
        let configurables_error = MyScriptConfigurables::new(encoder_config)
            .with_STRUCT(new_struct)
            .expect_err("should error");

        assert!(configurables_error
            .to_string()
            .contains("token limit `1` reached while encoding. Try increasing it"),)
    }
}\n```

Overriding storage slots

If you use storage in your contract, the default storage values will be generated in a JSON file (e.g. my_contract-storage_slots.json) by the Sway compiler. These are loaded automatically for you when you load a contract binary. If you wish to override some of the defaults, you need to provide the corresponding storage slots manually:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

If you don't have the slot storage file (my_contract-storage_slots.json example from above) for some reason, or you don't wish to load any of the default values, you can disable the auto-loading of storage slots:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Interacting with contracts

If you already have a deployed contract and want to call its methods using the SDK, but without deploying it again, all you need is the contract ID of your deployed contract. You can skip the whole deployment setup and call ::new(contract_id, wallet) directly. For example:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

The above example assumes that your contract ID string is encoded in the bech32 format. You can recognize it by the human-readable-part "fuel" followed by the separator "1". However, when using other Fuel tools, you might end up with a hex-encoded contract ID string. In that case, you can create your contract instance as follows:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

You can learn more about the Fuel SDK bech32 types here.

The FuelVM binary file

The command forc build compiles your Sway code and generates the bytecode: the binary code that the Fuel Virtual Machine will interpret. For instance, the smart contract below:

contract;

abi MyContract {
    fn test_function() -> bool;
}

impl MyContract for Contract {
    fn test_function() -> bool {
        true
    }
}

After forc build, will have a binary file that contains:

$ cat out/release/my-test.bin
G4]�]D`I]C�As@
           6]C�$@!QK%

This seems very unreadable! But, forc has a nice interpreter for this bytecode: forc parse-bytecode, which will interpret that binary data and output the equivalent FuelVM assembly:

$ forc parse-bytecode out/release/my-test.bin
half-word   byte   op                raw           notes
        0   0      JI(4)             90 00 00 04   jump to byte 16
        1   4      NOOP              47 00 00 00
        2   8      Undefined         00 00 00 00   data section offset lo (0)
        3   12     Undefined         00 00 00 34   data section offset hi (52)
        4   16     LW(63, 12, 1)     5d fc c0 01
        5   20     ADD(63, 63, 12)   10 ff f3 00
        6   24     LW(17, 6, 73)     5d 44 60 49
        7   28     LW(16, 63, 1)     5d 43 f0 01
        8   32     EQ(16, 17, 16)    13 41 14 00
        9   36     JNZI(16, 11)      73 40 00 0b   conditionally jump to byte 44
       10   40     RVRT(0)           36 00 00 00
       11   44     LW(16, 63, 0)     5d 43 f0 00
       12   48     RET(16)           24 40 00 00
       13   52     Undefined         00 00 00 00
       14   56     Undefined         00 00 00 01
       15   60     Undefined         00 00 00 00
       16   64     XOR(20, 27, 53)   21 51 bd 4b

If you want to deploy your smart contract using the SDK, this binary file is important; it's what we'll be sending to the FuelVM in a transaction.

Deploying Large Contracts

If your contract exceeds the size limit for a single deployment:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

you can deploy it in segments using a partitioned approach:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

When you convert a standard contract into a loader contract, the following changes occur:

  • The original contract code is replaced with the loader contract code.
  • The original contract code is split into blobs, which will be deployed via blob transactions before deploying the contract itself.
  • The new loader code, when invoked, loads these blobs into memory and executes your original contract.

After deploying the loader contract, you can interact with it just as you would with a standard contract:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

A helper function is available to deploy your contract normally if it is within the size limit, or as a loader contract if it exceeds the limit:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

You also have the option to separate the blob upload from the contract deployment for more granular control:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Alternatively, you can manually split your contract code into blobs and then create and deploy a loader:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Or you can upload the blobs yourself and proceed with just the loader deployment:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Blob Size Considerations

The size of a Blob transaction is constrained by three factors:

  1. The maximum size of a single transaction:
```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```
  1. The maximum gas usage for a single transaction:
```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```
  1. The maximum HTTP body size accepted by the Fuel node.

To estimate an appropriate size for your blobs, you can run:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

However, keep in mind the following limitations:

  • The estimation only considers the maximum transaction size, not the max gas usage or HTTP body limit.
  • It does not account for any size increase that may occur after the transaction is funded.

Therefore, it is advisable to make your blobs a few percent smaller than the estimated maximum size.

Calling contracts

Once you've deployed your contract, as seen in the previous sections, you'll likely want to:

  1. Call contract methods;
  2. Configure call parameters and transaction policies;
  3. Forward coins and gas in your contract calls;
  4. Read and interpret returned values and logs.

Here's an example. Suppose your Sway contract has two ABI methods called initialize_counter(u64) and increment_counter(u64). Once you've deployed it the contract, you can call these methods like this:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

The example above uses all the default configurations and performs a simple contract call.

Furthermore, if you need to separate submission from value retrieval for any reason, you can do so as follows:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Next, we'll see how we can further configure the many different parameters in a contract call.

Calls with different wallets

You can use the with_account() method on an existing contract instance as a shorthand for creating a new instance connected to the provided wallet. This lets you make contracts calls with different wallets in a chain like fashion.

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Note: connecting a different wallet to an existing instance ignores its set provider in favor of the provider used to deploy the contract. If you have two wallets connected to separate providers (each communicating with a separate fuel-core), the one assigned to the deploying wallet will also be used for contract calls. This behavior is only relevant if multiple providers (i.e. fuel-core instances) are present and can otherwise be ignored.

Transaction policies

Transaction policies are defined as follows:

```rust\nuse std::{collections::HashMap, fmt::Debug};

use async_trait::async_trait;
use fuel_crypto::{Message, Signature};
use fuel_tx::{
    field::{
        Inputs, Maturity, MintAmount, MintAssetId, Outputs, Policies as PoliciesField,
        Script as ScriptField, ScriptData, ScriptGasLimit, WitnessLimit, Witnesses,
    },
    input::{
        coin::{CoinPredicate, CoinSigned},
        message::{
            MessageCoinPredicate, MessageCoinSigned, MessageDataPredicate, MessageDataSigned,
        },
    },
    policies::PolicyType,
    Blob, Bytes32, Cacheable, Chargeable, ConsensusParameters, Create, FormatValidityChecks, Input,
    Mint, Output, Salt as FuelSalt, Script, StorageSlot, Transaction as FuelTransaction,
    TransactionFee, UniqueIdentifier, Upgrade, Upload, Witness,
};
use fuel_types::{bytes::padded_len_usize, AssetId, ChainId};
use itertools::Itertools;

use crate::{
    traits::Signer,
    types::{
        bech32::Bech32Address,
        errors::{error, error_transaction, Error, Result},
        DryRunner,
    },
    utils::{calculate_witnesses_size, sealed},
};

#[derive(Default, Debug, Clone)]
pub struct Transactions {
    fuel_transactions: Vec<FuelTransaction>,
}

impl Transactions {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn insert(mut self, tx: impl Into<FuelTransaction>) -> Self {
        self.fuel_transactions.push(tx.into());

        self
    }

    pub fn as_slice(&self) -> &[FuelTransaction] {
        &self.fuel_transactions
    }
}

#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct MintTransaction {
    tx: Box<Mint>,
}

impl From<MintTransaction> for FuelTransaction {
    fn from(mint: MintTransaction) -> Self {
        (*mint.tx).into()
    }
}

impl From<MintTransaction> for Mint {
    fn from(tx: MintTransaction) -> Self {
        *tx.tx
    }
}

impl From<Mint> for MintTransaction {
    fn from(tx: Mint) -> Self {
        Self { tx: Box::new(tx) }
    }
}

impl MintTransaction {
    pub fn check_without_signatures(
        &self,
        block_height: u32,
        consensus_parameters: &ConsensusParameters,
    ) -> Result<()> {
        Ok(self
            .tx
            .check_without_signatures(block_height.into(), consensus_parameters)?)
    }
    #[must_use]
    pub fn id(&self, chain_id: ChainId) -> Bytes32 {
        self.tx.id(&chain_id)
    }

    #[must_use]
    pub fn mint_asset_id(&self) -> &AssetId {
        self.tx.mint_asset_id()
    }

    #[must_use]
    pub fn mint_amount(&self) -> u64 {
        *self.tx.mint_amount()
    }
}

#[derive(Default, Debug, Copy, Clone)]
// ANCHOR: tx_policies_struct
pub struct TxPolicies {
    tip: Option<u64>,
    witness_limit: Option<u64>,
    maturity: Option<u64>,
    max_fee: Option<u64>,
    script_gas_limit: Option<u64>,
}
// ANCHOR_END: tx_policies_struct

impl TxPolicies {
    pub fn new(
        tip: Option<u64>,
        witness_limit: Option<u64>,
        maturity: Option<u64>,
        max_fee: Option<u64>,
        script_gas_limit: Option<u64>,
    ) -> Self {
        Self {
            tip,
            witness_limit,
            maturity,
            max_fee,
            script_gas_limit,
        }
    }

    pub fn with_tip(mut self, tip: u64) -> Self {
        self.tip = Some(tip);
        self
    }

    pub fn tip(&self) -> Option<u64> {
        self.tip
    }

    pub fn with_witness_limit(mut self, witness_limit: u64) -> Self {
        self.witness_limit = Some(witness_limit);
        self
    }

    pub fn witness_limit(&self) -> Option<u64> {
        self.witness_limit
    }

    pub fn with_maturity(mut self, maturity: u64) -> Self {
        self.maturity = Some(maturity);
        self
    }

    pub fn maturity(&self) -> Option<u64> {
        self.maturity
    }

    pub fn with_max_fee(mut self, max_fee: u64) -> Self {
        self.max_fee = Some(max_fee);
        self
    }

    pub fn max_fee(&self) -> Option<u64> {
        self.max_fee
    }

    pub fn with_script_gas_limit(mut self, script_gas_limit: u64) -> Self {
        self.script_gas_limit = Some(script_gas_limit);
        self
    }

    pub fn script_gas_limit(&self) -> Option<u64> {
        self.script_gas_limit
    }
}

use fuel_tx::field::{BytecodeWitnessIndex, Salt, StorageSlots};

use crate::types::coin_type_id::CoinTypeId;

#[derive(Debug, Clone)]
pub enum TransactionType {
    Script(ScriptTransaction),
    Create(CreateTransaction),
    Mint(MintTransaction),
    Upload(UploadTransaction),
    Upgrade(UpgradeTransaction),
    Blob(BlobTransaction),
    Unknown,
}

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait EstimablePredicates: sealed::Sealed {
    /// If a transaction contains predicates, we have to estimate them
    /// before sending the transaction to the node. The estimation will check
    /// all predicates and set the `predicate_gas_used` to the actual consumed gas.
    async fn estimate_predicates(
        &mut self,
        provider: impl DryRunner,
        latest_chain_executor_version: Option<u32>,
    ) -> Result<()>;
}

pub trait GasValidation: sealed::Sealed {
    fn validate_gas(&self, _gas_used: u64) -> Result<()>;
}

pub trait ValidatablePredicates: sealed::Sealed {
    /// If a transaction contains predicates, we can verify that these predicates validate, ie
    /// that they return `true`
    fn validate_predicates(
        self,
        consensus_parameters: &ConsensusParameters,
        block_height: u32,
    ) -> Result<()>;
}

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait Transaction:
    TryFrom<FuelTransaction, Error = Error>
    + Into<FuelTransaction>
    + EstimablePredicates
    + ValidatablePredicates
    + GasValidation
    + Clone
    + Debug
    + sealed::Sealed
{
    fn fee_checked_from_tx(
        &self,
        consensus_parameters: &ConsensusParameters,
        gas_price: u64,
    ) -> Option<TransactionFee>;

    fn max_gas(&self, consensus_parameters: &ConsensusParameters) -> u64;

    /// Performs all stateless transaction validity checks. This includes the validity
    /// of fields according to rules in the specification and validity of signatures.
    /// <https://github.com/FuelLabs/fuel-specs/blob/master/src/tx-format/transaction.md>
    fn check(&self, block_height: u32, consensus_parameters: &ConsensusParameters) -> Result<()>;

    fn id(&self, chain_id: ChainId) -> Bytes32;

    fn maturity(&self) -> u32;

    fn with_maturity(self, maturity: u32) -> Self;

    fn metered_bytes_size(&self) -> usize;

    fn inputs(&self) -> &Vec<Input>;

    fn outputs(&self) -> &Vec<Output>;

    fn witnesses(&self) -> &Vec<Witness>;

    fn max_fee(&self) -> Option<u64>;

    fn size(&self) -> usize;

    fn witness_limit(&self) -> Option<u64>;

    fn tip(&self) -> Option<u64>;

    fn is_using_predicates(&self) -> bool;

    /// Precompute transaction metadata. The metadata is required for
    /// `check_without_signatures` validation.
    fn precompute(&mut self, chain_id: &ChainId) -> Result<()>;

    /// Append witness and return the corresponding witness index
    fn append_witness(&mut self, witness: Witness) -> Result<usize>;

    fn used_coins(
        &self,
        base_asset_id: &AssetId,
    ) -> HashMap<(Bech32Address, AssetId), Vec<CoinTypeId>>;

    async fn sign_with(
        &mut self,
        signer: &(impl Signer + Send + Sync),
        chain_id: ChainId,
    ) -> Result<Signature>;
}

impl TryFrom<TransactionType> for FuelTransaction {
    type Error = Error;
    fn try_from(value: TransactionType) -> Result<Self> {
        match value {
            TransactionType::Script(tx) => Ok(tx.into()),
            TransactionType::Create(tx) => Ok(tx.into()),
            TransactionType::Mint(tx) => Ok(tx.into()),
            TransactionType::Upload(tx) => Ok(tx.into()),
            TransactionType::Upgrade(tx) => Ok(tx.into()),
            TransactionType::Blob(tx) => Ok(tx.into()),
            TransactionType::Unknown => Err(error_transaction!(Other, "`Unknown` transaction")),
        }
    }
}

fn extract_coin_type_id(input: &Input) -> Option<CoinTypeId> {
    if let Some(utxo_id) = input.utxo_id() {
        return Some(CoinTypeId::UtxoId(*utxo_id));
    } else if let Some(nonce) = input.nonce() {
        return Some(CoinTypeId::Nonce(*nonce));
    }

    None
}

pub fn extract_owner_or_recipient(input: &Input) -> Option<Bech32Address> {
    let addr = match input {
        Input::CoinSigned(CoinSigned { owner, .. })
        | Input::CoinPredicate(CoinPredicate { owner, .. }) => Some(owner),
        Input::MessageCoinSigned(MessageCoinSigned { recipient, .. })
        | Input::MessageCoinPredicate(MessageCoinPredicate { recipient, .. })
        | Input::MessageDataSigned(MessageDataSigned { recipient, .. })
        | Input::MessageDataPredicate(MessageDataPredicate { recipient, .. }) => Some(recipient),
        Input::Contract(_) => None,
    };

    addr.map(|addr| Bech32Address::from(*addr))
}

macro_rules! impl_tx_wrapper {
    ($wrapper: ident, $wrapped: ident) => {
        #[derive(Debug, Clone)]
        pub struct $wrapper {
            pub(crate) tx: $wrapped,
            pub(crate) is_using_predicates: bool,
        }

        impl From<$wrapper> for $wrapped {
            fn from(tx: $wrapper) -> Self {
                tx.tx
            }
        }

        impl From<$wrapper> for FuelTransaction {
            fn from(tx: $wrapper) -> Self {
                tx.tx.into()
            }
        }

        impl TryFrom<FuelTransaction> for $wrapper {
            type Error = Error;

            fn try_from(tx: FuelTransaction) -> Result<Self> {
                match tx {
                    FuelTransaction::$wrapped(tx) => Ok(tx.into()),
                    _ => Err(error_transaction!(
                        Other,
                        "couldn't convert Transaction into a wrapper of type $wrapper"
                    )),
                }
            }
        }

        impl From<$wrapped> for $wrapper {
            fn from(tx: $wrapped) -> Self {
                let is_using_predicates = tx.inputs().iter().any(|input| {
                    matches!(
                        input,
                        Input::CoinPredicate { .. }
                            | Input::MessageCoinPredicate { .. }
                            | Input::MessageDataPredicate { .. }
                    )
                });

                $wrapper {
                    tx,
                    is_using_predicates,
                }
            }
        }

        impl ValidatablePredicates for $wrapper {
            fn validate_predicates(
                self,
                _consensus_parameters: &ConsensusParameters,
                _block_height: u32,
            ) -> Result<()> {
                // Can no longer validate predicates locally due to the need for blob storage

                Ok(())
            }
        }

        impl sealed::Sealed for $wrapper {}

        #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
        #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
        impl Transaction for $wrapper {
            fn max_gas(&self, consensus_parameters: &ConsensusParameters) -> u64 {
                self.tx.max_gas(
                    consensus_parameters.gas_costs(),
                    consensus_parameters.fee_params(),
                )
            }

            fn fee_checked_from_tx(
                &self,
                consensus_parameters: &ConsensusParameters,
                gas_price: u64,
            ) -> Option<TransactionFee> {
                TransactionFee::checked_from_tx(
                    &consensus_parameters.gas_costs(),
                    consensus_parameters.fee_params(),
                    &self.tx,
                    gas_price,
                )
            }

            fn check(
                &self,
                block_height: u32,
                consensus_parameters: &ConsensusParameters,
            ) -> Result<()> {
                Ok(self.tx.check(block_height.into(), consensus_parameters)?)
            }

            fn id(&self, chain_id: ChainId) -> Bytes32 {
                self.tx.id(&chain_id)
            }

            fn maturity(&self) -> u32 {
                (*self.tx.maturity()).into()
            }

            fn with_maturity(mut self, maturity: u32) -> Self {
                self.tx.set_maturity(maturity.into());
                self
            }

            fn metered_bytes_size(&self) -> usize {
                self.tx.metered_bytes_size()
            }

            fn inputs(&self) -> &Vec<Input> {
                self.tx.inputs()
            }

            fn outputs(&self) -> &Vec<Output> {
                self.tx.outputs()
            }

            fn witnesses(&self) -> &Vec<Witness> {
                self.tx.witnesses()
            }

            fn is_using_predicates(&self) -> bool {
                self.is_using_predicates
            }

            fn precompute(&mut self, chain_id: &ChainId) -> Result<()> {
                Ok(self.tx.precompute(chain_id)?)
            }

            fn max_fee(&self) -> Option<u64> {
                self.tx.policies().get(PolicyType::MaxFee)
            }

            fn size(&self) -> usize {
                use fuel_types::canonical::Serialize;
                self.tx.size()
            }

            fn witness_limit(&self) -> Option<u64> {
                self.tx.policies().get(PolicyType::WitnessLimit)
            }

            fn tip(&self) -> Option<u64> {
                self.tx.policies().get(PolicyType::Tip)
            }

            fn append_witness(&mut self, witness: Witness) -> Result<usize> {
                let witness_size = calculate_witnesses_size(
                    self.tx.witnesses().iter().chain(std::iter::once(&witness)),
                );
                let new_witnesses_size = padded_len_usize(witness_size)
                    .ok_or_else(|| error!(Other, "witness size overflow: {witness_size}"))?
                    as u64;

                if new_witnesses_size > self.tx.witness_limit() {
                    Err(error_transaction!(
                        Validation,
                        "Witness limit exceeded. Consider setting the limit manually with \
                        a transaction builder. The new limit should be: `{new_witnesses_size}`"
                    ))
                } else {
                    let idx = self.tx.witnesses().len();
                    self.tx.witnesses_mut().push(witness);

                    Ok(idx)
                }
            }

            fn used_coins(
                &self,
                base_asset_id: &AssetId,
            ) -> HashMap<(Bech32Address, AssetId), Vec<CoinTypeId>> {
                self.inputs()
                    .iter()
                    .filter_map(|input| match input {
                        Input::Contract { .. } => None,
                        _ => {
                            // Not a contract, it's safe to expect.
                            let owner = extract_owner_or_recipient(input).expect("has owner");
                            let asset_id = input
                                .asset_id(base_asset_id)
                                .expect("has `asset_id`")
                                .to_owned();

                            let id = extract_coin_type_id(input).unwrap();
                            Some(((owner, asset_id), id))
                        }
                    })
                    .into_group_map()
            }

            async fn sign_with(
                &mut self,
                signer: &(impl Signer + Send + Sync),
                chain_id: ChainId,
            ) -> Result<Signature> {
                let tx_id = self.id(chain_id);
                let message = Message::from_bytes(*tx_id);
                let signature = signer.sign(message).await?;

                self.append_witness(signature.as_ref().into())?;

                Ok(signature)
            }
        }
    };
}

impl_tx_wrapper!(ScriptTransaction, Script);
impl_tx_wrapper!(CreateTransaction, Create);
impl_tx_wrapper!(UploadTransaction, Upload);
impl_tx_wrapper!(UpgradeTransaction, Upgrade);
impl_tx_wrapper!(BlobTransaction, Blob);

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl EstimablePredicates for UploadTransaction {
    async fn estimate_predicates(
        &mut self,
        provider: impl DryRunner,
        latest_chain_executor_version: Option<u32>,
    ) -> Result<()> {
        let tx = provider
            .estimate_predicates(&self.tx.clone().into(), latest_chain_executor_version)
            .await?;

        tx.as_upload().expect("is upload").clone_into(&mut self.tx);

        Ok(())
    }
}

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl EstimablePredicates for UpgradeTransaction {
    async fn estimate_predicates(
        &mut self,
        provider: impl DryRunner,
        latest_chain_executor_version: Option<u32>,
    ) -> Result<()> {
        let tx = provider
            .estimate_predicates(&self.tx.clone().into(), latest_chain_executor_version)
            .await?;

        tx.as_upgrade()
            .expect("is upgrade")
            .clone_into(&mut self.tx);

        Ok(())
    }
}

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl EstimablePredicates for CreateTransaction {
    async fn estimate_predicates(
        &mut self,
        provider: impl DryRunner,
        latest_chain_executor_version: Option<u32>,
    ) -> Result<()> {
        let tx = provider
            .estimate_predicates(&self.tx.clone().into(), latest_chain_executor_version)
            .await?;

        tx.as_create().expect("is create").clone_into(&mut self.tx);

        Ok(())
    }
}

impl CreateTransaction {
    pub fn salt(&self) -> &FuelSalt {
        self.tx.salt()
    }

    pub fn bytecode_witness_index(&self) -> u16 {
        *self.tx.bytecode_witness_index()
    }

    pub fn storage_slots(&self) -> &Vec<StorageSlot> {
        self.tx.storage_slots()
    }
}

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl EstimablePredicates for ScriptTransaction {
    async fn estimate_predicates(
        &mut self,
        provider: impl DryRunner,
        latest_chain_executor_version: Option<u32>,
    ) -> Result<()> {
        let tx = provider
            .estimate_predicates(&self.tx.clone().into(), latest_chain_executor_version)
            .await?;

        tx.as_script().expect("is script").clone_into(&mut self.tx);

        Ok(())
    }
}

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl EstimablePredicates for BlobTransaction {
    async fn estimate_predicates(
        &mut self,
        provider: impl DryRunner,
        latest_chain_executor_version: Option<u32>,
    ) -> Result<()> {
        let tx = provider
            .estimate_predicates(&self.tx.clone().into(), latest_chain_executor_version)
            .await?;

        tx.as_blob().expect("is blob").clone_into(&mut self.tx);

        Ok(())
    }
}

impl GasValidation for CreateTransaction {
    fn validate_gas(&self, _gas_used: u64) -> Result<()> {
        Ok(())
    }
}

impl GasValidation for UploadTransaction {
    fn validate_gas(&self, _gas_used: u64) -> Result<()> {
        Ok(())
    }
}

impl GasValidation for UpgradeTransaction {
    fn validate_gas(&self, _gas_used: u64) -> Result<()> {
        Ok(())
    }
}

impl GasValidation for BlobTransaction {
    fn validate_gas(&self, _gas_used: u64) -> Result<()> {
        Ok(())
    }
}

impl GasValidation for ScriptTransaction {
    fn validate_gas(&self, gas_used: u64) -> Result<()> {
        if gas_used > *self.tx.script_gas_limit() {
            return Err(error_transaction!(
                Validation,
                "script_gas_limit({}) is lower than the estimated gas_used({})",
                self.tx.script_gas_limit(),
                gas_used
            ));
        }

        Ok(())
    }
}

impl ScriptTransaction {
    pub fn script(&self) -> &Vec<u8> {
        self.tx.script()
    }

    pub fn script_data(&self) -> &Vec<u8> {
        self.tx.script_data()
    }

    pub fn gas_limit(&self) -> u64 {
        *self.tx.script_gas_limit()
    }

    pub fn with_gas_limit(mut self, gas_limit: u64) -> Self {
        *self.tx.script_gas_limit_mut() = gas_limit;
        self
    }
}

#[cfg(test)]
mod test {

    use fuel_tx::policies::Policies;

    use super::*;

    #[test]
    fn append_witnesses_returns_error_when_limit_exceeded() {
        let mut tx = ScriptTransaction {
            tx: FuelTransaction::script(
                0,
                vec![],
                vec![],
                Policies::default(),
                vec![],
                vec![],
                vec![],
            ),
            is_using_predicates: false,
        };

        let witness = vec![0, 1, 2].into();
        let err = tx.append_witness(witness).expect_err("should error");

        let expected_err_str = "transaction validation: Witness limit exceeded. \
                                Consider setting the limit manually with a transaction builder. \
                                The new limit should be: `16`";

        assert_eq!(&err.to_string(), expected_err_str);
    }
}\n```

Where:

  1. Tip - amount to pay the block producer to prioritize the transaction.
  2. Witness Limit - The maximum amount of witness data allowed for the transaction.
  3. Maturity - Block until which the transaction cannot be included.
  4. Max Fee - The maximum fee payable by this transaction.
  5. Script Gas Limit - The maximum amount of gas the transaction may consume for executing its script code.

When the Script Gas Limit is not set, the Rust SDK will estimate the consumed gas in the background and set it as the limit.

If the Witness Limit is not set, the SDK will set it to the size of all witnesses and signatures defined in the transaction builder.

You can configure these parameters by creating an instance of TxPolicies and passing it to a chain method called with_tx_policies:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

You can also use TxPolicies::default() to use the default values.

This way:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

As you might have noticed, TxPolicies can also be specified when deploying contracts or transferring assets by passing it to the respective methods.

Call parameters

The parameters for a contract call are:

  1. Amount
  2. Asset ID
  3. Gas forwarded

You can use these to forward coins to a contract. You can configure these parameters by creating an instance of CallParameters and passing it to a chain method called call_params.

For instance, suppose the following contract that uses Sway's msg_amount() to return the amount sent in that transaction.

```sway\ncontract;

use std::storage::storage_api::{read, write};
use std::context::msg_amount;

struct MyType {
    x: u64,
    y: u64,
}

#[allow(dead_code)]
struct Person {
    name: str[4],
}

#[allow(dead_code)]
enum State {
    A: (),
    B: (),
    C: (),
}

abi TestContract {
    #[storage(write)]
    fn initialize_counter(value: u64) -> u64;
    #[storage(read, write)]
    fn increment_counter(value: u64) -> u64;
    #[storage(read)]
    fn get_counter() -> u64;
    // ANCHOR: low_level_call
    #[storage(write)]
    fn set_value_multiple_complex(a: MyStruct, b: str[4]);
    // ANCHOR_END: low_level_call
    #[storage(read)]
    fn get_str_value() -> str[4];
    #[storage(read)]
    fn get_bool_value() -> bool;
    #[storage(read)]
    fn get_value() -> u64;
    fn get(x: u64, y: u64) -> u64;
    fn get_alt(x: MyType) -> MyType;
    fn get_single(x: u64) -> u64;
    fn array_of_structs(p: [Person; 2]) -> [Person; 2];
    fn array_of_enums(p: [State; 2]) -> [State; 2];
    fn get_array(p: [u64; 2]) -> [u64; 2];
    #[payable]
    fn get_msg_amount() -> u64;
    fn new() -> u64;
}

const COUNTER_KEY = 0x0000000000000000000000000000000000000000000000000000000000000000;

storage {
    value: u64 = 0,
    value_str: str[4] = __to_str_array("none"),
    value_bool: bool = false,
}

pub struct MyStruct {
    a: bool,
    b: [u64; 3],
}

impl TestContract for Contract {
    // ANCHOR: msg_amount
    #[payable]
    fn get_msg_amount() -> u64 {
        msg_amount()
    }
    // ANCHOR_END: msg_amount
    #[storage(write)]
    fn initialize_counter(value: u64) -> u64 {
        write(COUNTER_KEY, 0, value);
        value
    }

    /// This method will read the counter from storage, increment it
    /// and write the incremented value to storage
    #[storage(read, write)]
    fn increment_counter(value: u64) -> u64 {
        let new_value = read::<u64>(COUNTER_KEY, 0).unwrap_or(0) + value;
        write(COUNTER_KEY, 0, new_value);
        new_value
    }

    #[storage(read)]
    fn get_counter() -> u64 {
        read::<u64>(COUNTER_KEY, 0).unwrap_or(0)
    }

    #[storage(write)]
    fn set_value_multiple_complex(a: MyStruct, b: str[4]) {
        storage.value.write(a.b[1]);
        storage.value_str.write(b);
        storage.value_bool.write(a.a);
    }

    #[storage(read)]
    fn get_str_value() -> str[4] {
        storage.value_str.read()
    }

    #[storage(read)]
    fn get_bool_value() -> bool {
        storage.value_bool.read()
    }

    #[storage(read)]
    fn get_value() -> u64 {
        storage.value.read()
    }

    fn get(x: u64, y: u64) -> u64 {
        x + y
    }

    fn get_alt(t: MyType) -> MyType {
        t
    }

    fn get_single(x: u64) -> u64 {
        x
    }

    fn array_of_structs(p: [Person; 2]) -> [Person; 2] {
        p
    }

    fn array_of_enums(p: [State; 2]) -> [State; 2] {
        p
    }

    fn get_array(p: [u64; 2]) -> [u64; 2] {
        p
    }

    fn new() -> u64 {
        12345u64
    }
}\n```

Then, in Rust, after setting up and deploying the above contract, you can configure the amount being sent in the transaction like this:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

call_params returns a result to ensure you don't forward assets to a contract method that isn't payable.

In the following example, we try to forward an amount of 100 of the base asset to non_payable. As its name suggests, non_payable isn't annotated with #[payable] in the contract code. Passing CallParameters with an amount other than 0 leads to an error:

```rust\nuse std::time::Duration;

use fuel_tx::{
    consensus_parameters::{ConsensusParametersV1, FeeParametersV1},
    ConsensusParameters, FeeParameters, Output,
};
use fuels::{
    core::codec::{calldata, encode_fn_selector, DecoderConfig, EncoderConfig},
    prelude::*,
    programs::DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
    tx::ContractParameters,
    types::{errors::transaction::Reason, input::Input, Bits256, Identity},
};
use tokio::time::Instant;

#[tokio::test]
async fn test_multiple_args() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Make sure we can call the contract with multiple arguments
    let contract_methods = contract_instance.methods();
    let response = contract_methods.get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    let t = MyType { x: 5, y: 6 };
    let response = contract_methods.get_alt(t.clone()).call().await?;
    assert_eq!(response.value, t);

    let response = contract_methods.get_single(5).call().await?;
    assert_eq!(response.value, 5);
    Ok(())
}

#[tokio::test]
async fn test_contract_calling_contract() -> Result<()> {
    // Tests a contract call that calls another contract (FooCaller calls FooContract underneath)
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "lib_contract_instance2",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();
    let lib_contract_id2 = lib_contract_instance2.contract_id();

    // Call the contract directly. It increments the given value.
    let response = lib_contract_instance.methods().increment(42).call().await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance
        .methods()
        .increment_from_contracts(lib_contract_id, lib_contract_id2, 42)
        // Note that the two lib_contract_instances have different types
        .with_contracts(&[&lib_contract_instance, &lib_contract_instance2])
        .call()
        .await?;

    assert_eq!(86, response.value);

    // ANCHOR: external_contract
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    assert_eq!(43, response.value);

    // ANCHOR: external_contract_ids
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contract_ids(&[lib_contract_id.clone()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    assert_eq!(43, response.value);
    Ok(())
}

#[tokio::test]
async fn test_reverting_transaction() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertContract",
            project = "e2e/sway/contracts/revert_transaction_error"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .make_transaction_fail(true)
        .call()
        .await;

    assert!(matches!(
        response,
        Err(Error::Transaction(Reason::Reverted { revert_id, .. })) if revert_id == 128
    ));

    Ok(())
}

#[tokio::test]
async fn test_multiple_read_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MultiReadContract",
            project = "e2e/sway/contracts/multiple_read_calls"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MultiReadContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    contract_methods.store(42).call().await?;

    // Use "simulate" because the methods don't actually
    // run a transaction, but just a dry-run
    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);

    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_multi_call_beginner() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let (val_1, val_2): (u64, u64) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_pro() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let my_type_1 = MyType { x: 1, y: 2 };
    let my_type_2 = MyType { x: 3, y: 4 };

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(5);
    let call_handler_2 = contract_methods.get_single(6);
    let call_handler_3 = contract_methods.get_alt(my_type_1.clone());
    let call_handler_4 = contract_methods.get_alt(my_type_2.clone());
    let call_handler_5 = contract_methods.get_array([7; 2]);
    let call_handler_6 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2)
        .add_call(call_handler_3)
        .add_call(call_handler_4)
        .add_call(call_handler_5)
        .add_call(call_handler_6);

    let (val_1, val_2, type_1, type_2, array_1, array_2): (
        u64,
        u64,
        MyType,
        MyType,
        [u64; 2],
        [u64; 2],
    ) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 5);
    assert_eq!(val_2, 6);
    assert_eq!(type_1, my_type_1);
    assert_eq!(type_2, my_type_2);
    assert_eq!(array_1, [7; 2]);
    assert_eq!(array_2, [42; 2]);

    Ok(())
}

#[tokio::test]
async fn test_contract_call_fee_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let gas_limit = 800;
    let tolerance = Some(0.2);
    let block_horizon = Some(1);
    let expected_gas_used = 960;
    let expected_metered_bytes_size = 824;

    let estimated_transaction_cost = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit))
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?;

    assert_eq!(estimated_transaction_cost.gas_used, expected_gas_used);
    assert_eq!(
        estimated_transaction_cost.metered_bytes_size,
        expected_metered_bytes_size
    );

    Ok(())
}

#[tokio::test]
async fn contract_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let estimated_gas_used = contract_methods
        .initialize_counter(42)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn mult_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.initialize_counter(42);
    let call_handler_2 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let tolerance = Some(0.0);
    let block_horizon = Some(1);
    let estimated_gas_used = multi_call_handler
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = multi_call_handler.call::<(u64, [u64; 2])>().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn contract_method_call_respects_maturity() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BlockHeightContract",
            project = "e2e/sway/contracts/transaction_block_height"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BlockHeightContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_w_maturity = |maturity| {
        contract_instance
            .methods()
            .calling_this_will_produce_a_block()
            .with_tx_policies(TxPolicies::default().with_maturity(maturity))
    };

    call_w_maturity(1).call().await.expect(
        "should have passed since we're calling with a maturity \
         that is less or equal to the current block height",
    );

    call_w_maturity(3).call().await.expect_err(
        "should have failed since we're calling with a maturity \
         that is greater than the current block height",
    );

    Ok(())
}

#[tokio::test]
async fn test_auth_msg_sender_from_sdk() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "AuthContract",
            project = "e2e/sway/contracts/auth_testing_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "AuthContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Contract returns true if `msg_sender()` matches `wallet.address()`.
    let response = contract_instance
        .methods()
        .check_msg_sender(wallet.address())
        .call()
        .await?;

    assert!(response.value);
    Ok(())
}

#[tokio::test]
async fn test_large_return_data() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/large_return_data"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let res = contract_methods.get_id().call().await?;

    assert_eq!(
        res.value.0,
        [
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ]
    );

    // One word-sized string
    let res = contract_methods.get_small_string().call().await?;
    assert_eq!(res.value, "gggggggg");

    // Two word-sized string
    let res = contract_methods.get_large_string().call().await?;
    assert_eq!(res.value, "ggggggggg");

    // Large struct will be bigger than a `WORD`.
    let res = contract_methods.get_large_struct().call().await?;
    assert_eq!(res.value.foo, 12);
    assert_eq!(res.value.bar, 42);

    // Array will be returned in `ReturnData`.
    let res = contract_methods.get_large_array().call().await?;
    assert_eq!(res.value, [1, 2]);

    let res = contract_methods.get_contract_id().call().await?;

    // First `value` is from `CallResponse`.
    // Second `value` is from the `ContractId` type.
    assert_eq!(
        res.value,
        ContractId::from([
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ])
    );
    Ok(())
}

#[tokio::test]
async fn can_handle_function_called_new() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().new().call().await?.value;

    assert_eq!(response, 12345);
    Ok(())
}

#[tokio::test]
async fn test_contract_setup_macro_deploy_with_salt() -> Result<()> {
    // ANCHOR: contract_setup_macro_multi
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_caller_id = contract_caller_instance.contract_id();

    let contract_caller_id2 = contract_caller_instance2.contract_id();

    // Because we deploy with salt, we can deploy the same contract multiple times
    assert_ne!(contract_caller_id, contract_caller_id2);

    // The first contract can be called because they were deployed on the same provider
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance2
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);
    // ANCHOR_END: contract_setup_macro_multi

    Ok(())
}

#[tokio::test]
async fn test_wallet_getter() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(contract_instance.account().address(), wallet.address());
    //`contract_id()` is tested in
    // async fn test_contract_calling_contract() -> Result<()> {
    Ok(())
}

#[tokio::test]
async fn test_connect_wallet() -> Result<()> {
    // ANCHOR: contract_setup_macro_manual_wallet
    let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));

    let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
    let wallet = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    // ANCHOR_END: contract_setup_macro_manual_wallet

    // pay for call with wallet
    let tx_policies = TxPolicies::default()
        .with_tip(100)
        .with_script_gas_limit(1_000_000);

    contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm that funds have been deducted
    let wallet_balance = wallet.get_asset_balance(&Default::default()).await?;
    assert!(DEFAULT_COIN_AMOUNT > wallet_balance);

    // pay for call with wallet_2
    contract_instance
        .with_account(wallet_2.clone())
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm there are no changes to wallet, wallet_2 has been charged
    let wallet_balance_second_call = wallet.get_asset_balance(&Default::default()).await?;
    let wallet_2_balance = wallet_2.get_asset_balance(&Default::default()).await?;
    assert_eq!(wallet_balance_second_call, wallet_balance);
    assert!(DEFAULT_COIN_AMOUNT > wallet_2_balance);

    Ok(())
}

async fn setup_output_variable_estimation_test() -> Result<(
    Vec<WalletUnlocked>,
    [Identity; 3],
    AssetId,
    Bech32ContractId,
)> {
    let wallet_config = WalletsConfig::new(Some(3), None, None);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;

    let contract_id = Contract::load_from(
        "sway/contracts/token_ops/out/release/token_ops.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallets[0], TxPolicies::default())
    .await?;

    let mint_asset_id = contract_id.asset_id(&Bits256::zeroed());
    let addresses = wallets
        .iter()
        .map(|wallet| wallet.address().into())
        .collect::<Vec<_>>()
        .try_into()
        .unwrap();

    Ok((wallets, addresses, mint_asset_id, contract_id))
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());
    let contract_methods = contract_instance.methods();
    let amount = 1000;

    {
        // Should fail due to lack of output variables
        let response = contract_methods
            .mint_to_addresses(amount, addresses)
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    {
        // Should add 3 output variables automatically
        let _ = contract_methods
            .mint_to_addresses(amount, addresses)
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .call()
            .await?;

        for wallet in wallets.iter() {
            let balance = wallet.get_asset_balance(&mint_asset_id).await?;
            assert_eq!(balance, amount);
        }
    }

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallets[0].clone());
    let contract_methods = contract_instance.methods();
    const NUM_OF_CALLS: u64 = 3;
    let amount = 1000;
    let total_amount = amount * NUM_OF_CALLS;

    let mut multi_call_handler = CallHandler::new_multi_call(wallets[0].clone());
    for _ in 0..NUM_OF_CALLS {
        let call_handler = contract_methods.mint_to_addresses(amount, addresses);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    wallets[0]
        .force_transfer_to_contract(
            &contract_id,
            total_amount,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await
        .unwrap();

    let base_layer_address = Bits256([1u8; 32]);
    let call_handler = contract_methods.send_message(base_layer_address, amount);
    multi_call_handler = multi_call_handler.add_call(call_handler);

    let _ = multi_call_handler
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call::<((), (), ())>()
        .await?;

    for wallet in wallets.iter() {
        let balance = wallet.get_asset_balance(&mint_asset_id).await?;
        assert_eq!(balance, 3 * amount);
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_instance_get_balances() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let (coins, asset_ids) = setup_multiple_assets_coins(wallet.address(), 2, 4, 8);

    let random_asset_id = &asset_ids[1];
    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_id = contract_instance.contract_id();

    // Check the current balance of the contract with id 'contract_id'
    let contract_balances = contract_instance.get_balances().await?;
    assert!(contract_balances.is_empty());

    // Transfer an amount to the contract
    let amount = 8;
    wallet
        .force_transfer_to_contract(contract_id, amount, *random_asset_id, TxPolicies::default())
        .await?;

    // Check that the contract now has 1 coin
    let contract_balances = contract_instance.get_balances().await?;
    assert_eq!(contract_balances.len(), 1);

    let random_asset_balance = contract_balances.get(random_asset_id).unwrap();
    assert_eq!(*random_asset_balance, amount);

    Ok(())
}

#[tokio::test]
async fn contract_call_futures_implement_send() -> Result<()> {
    use std::future::Future;

    fn tokio_spawn_imitation<T>(_: T)
    where
        T: Future + Send + 'static,
    {
    }

    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    tokio_spawn_imitation(async move {
        contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await
            .unwrap();
    });
    Ok(())
}

#[tokio::test]
async fn test_contract_set_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let res = lib_contract_instance.methods().increment(42).call().await?;
    assert_eq!(43, res.value);

    {
        // Should fail due to missing external contracts
        let res = contract_caller_instance
            .methods()
            .increment_from_contract(lib_contract_id, 42)
            .call()
            .await;

        assert!(matches!(
            res,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    let res = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    assert_eq!(43, res.value);
    Ok(())
}

#[tokio::test]
async fn test_output_variable_contract_id_estimation_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_test_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_methods = contract_caller_instance.methods();

    let mut multi_call_handler =
        CallHandler::new_multi_call(wallet.clone()).with_tx_policies(Default::default());

    for _ in 0..3 {
        let call_handler = contract_methods.increment_from_contract(lib_contract_id, 42);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    // add call that does not need ContractId
    let contract_methods = contract_test_instance.methods();
    let call_handler = contract_methods.get(5, 6);

    multi_call_handler = multi_call_handler.add_call(call_handler);

    let call_response = multi_call_handler
        .determine_missing_contracts(None)
        .await?
        .call::<(u64, u64, u64, u64)>()
        .await?;

    assert_eq!(call_response.value, (43, 43, 43, 11));

    Ok(())
}

#[tokio::test]
async fn test_contract_call_with_non_default_max_input() -> Result<()> {
    use fuels::{
        tx::{ConsensusParameters, TxParameters},
        types::coin::Coin,
    };

    let mut consensus_parameters = ConsensusParameters::default();
    let tx_params = TxParameters::default()
        .with_max_inputs(123)
        .with_max_size(1_000_000);
    consensus_parameters.set_tx_params(tx_params);
    let contract_params = ContractParameters::default().with_contract_max_size(1_000_000);
    consensus_parameters.set_contract_params(contract_params);

    let mut wallet = WalletUnlocked::new_random(None);

    let coins: Vec<Coin> = setup_single_asset_coins(
        wallet.address(),
        Default::default(),
        DEFAULT_NUM_COINS,
        DEFAULT_COIN_AMOUNT,
    );
    let chain_config = ChainConfig {
        consensus_parameters: consensus_parameters.clone(),
        ..ChainConfig::default()
    };

    let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
    wallet.set_provider(provider.clone());
    assert_eq!(consensus_parameters, provider.consensus_parameters().await?);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn test_add_custom_assets() -> Result<()> {
    let initial_amount = 100_000;
    let asset_base = AssetConfig {
        id: AssetId::zeroed(),
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_1 = AssetId::from([3u8; 32]);
    let asset_1 = AssetConfig {
        id: asset_id_1,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_2 = AssetId::from([1u8; 32]);
    let asset_2 = AssetConfig {
        id: asset_id_2,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let assets = vec![asset_base, asset_1, asset_2];

    let num_wallets = 2;
    let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
    let mut wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
    let wallet_1 = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet_1",
            random_salt = false,
        ),
    );

    let amount_1 = 5000;
    let amount_2 = 3000;
    let response = contract_instance
        .methods()
        .get(5, 6)
        .add_custom_asset(asset_id_1, amount_1, Some(wallet_2.address().clone()))
        .add_custom_asset(asset_id_2, amount_2, Some(wallet_2.address().clone()))
        .call()
        .await?;

    assert_eq!(response.value, 11);

    let balance_asset_1 = wallet_1.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_1.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount - amount_1);
    assert_eq!(balance_asset_2, initial_amount - amount_2);

    let balance_asset_1 = wallet_2.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_2.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount + amount_1);
    assert_eq!(balance_asset_2, initial_amount + amount_2);

    Ok(())
}

#[tokio::test]
async fn contract_load_error_messages() {
    {
        let binary_path = "sway/contracts/contract_test/out/release/no_file_on_path.bin";
        let expected_error = format!("io: file \"{binary_path}\" does not exist");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
    {
        let binary_path = "sway/contracts/contract_test/out/release/contract_test-abi.json";
        let expected_error = format!("expected \"{binary_path}\" to have '.bin' extension");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
}

#[tokio::test]
async fn test_payable_annotation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/payable_annotation"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .payable()
        .call_params(
            CallParameters::default()
                .with_amount(100)
                .with_gas_forwarded(20_000),
        )?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    // ANCHOR: non_payable_params
    let err = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_amount(100))
        .expect_err("should return error");

    assert!(matches!(err, Error::Other(s) if s.contains("assets forwarded to non-payable method")));
    // ANCHOR_END: non_payable_params

    let response = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_gas_forwarded(20_000))?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    Ok(())
}

#[tokio::test]
async fn multi_call_from_calls_with_different_account_types() -> Result<()> {
    use fuels::prelude::*;

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallet = WalletUnlocked::new_random(None);
    let predicate = Predicate::from_code(vec![]);

    let contract_methods_wallet =
        MyContract::new(Bech32ContractId::default(), wallet.clone()).methods();
    let contract_methods_predicate =
        MyContract::new(Bech32ContractId::default(), predicate).methods();

    let call_handler_1 = contract_methods_wallet.initialize_counter(42);
    let call_handler_2 = contract_methods_predicate.get_array([42; 2]);

    let _multi_call_handler = CallHandler::new_multi_call(wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    Ok(())
}

#[tokio::test]
async fn low_level_call() -> Result<()> {
    use fuels::types::SizedAsciiString;

    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyCallerContract",
                project = "e2e/sway/contracts/low_level_caller"
            ),
            Contract(
                name = "MyTargetContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "caller_contract_instance",
            contract = "MyCallerContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "target_contract_instance",
            contract = "MyTargetContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let function_selector = encode_fn_selector("initialize_counter");
    let call_data = calldata!(42u64)?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let response = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await?;
    assert_eq!(response.value, 42);

    let function_selector = encode_fn_selector("set_value_multiple_complex");
    let call_data = calldata!(
        MyStruct {
            a: true,
            b: [1, 2, 3],
        },
        SizedAsciiString::<4>::try_from("fuel")?
    )?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let result_uint = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await
        .unwrap()
        .value;

    let result_bool = target_contract_instance
        .methods()
        .get_bool_value()
        .call()
        .await
        .unwrap()
        .value;

    let result_str = target_contract_instance
        .methods()
        .get_str_value()
        .call()
        .await
        .unwrap()
        .value;

    assert_eq!(result_uint, 42);
    assert!(result_bool);
    assert_eq!(result_str, "fuel");

    Ok(())
}

#[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
#[test]
fn db_rocksdb() {
    use std::{fs, str::FromStr};

    use fuels::{
        accounts::wallet::WalletUnlocked,
        client::{PageDirection, PaginationRequest},
        crypto::SecretKey,
        prelude::{setup_test_provider, DbType, Error, ViewOnlyAccount, DEFAULT_COIN_AMOUNT},
    };

    let temp_dir = tempfile::tempdir().expect("failed to make tempdir");
    let temp_dir_name = temp_dir
        .path()
        .file_name()
        .expect("failed to get file name")
        .to_string_lossy()
        .to_string();
    let temp_database_path = temp_dir.path().join("db");

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let _ = temp_dir;
            let wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            const NUMBER_OF_ASSETS: u64 = 2;
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let chain_config = ChainConfig {
                chain_name: temp_dir_name.clone(),
                consensus_parameters: Default::default(),
                ..ChainConfig::local_testnet()
            };

            let (coins, _) = setup_multiple_assets_coins(
                wallet.address(),
                NUMBER_OF_ASSETS,
                DEFAULT_NUM_COINS,
                DEFAULT_COIN_AMOUNT,
            );

            let provider =
                setup_test_provider(coins.clone(), vec![], Some(node_config), Some(chain_config))
                    .await?;

            provider.produce_blocks(2, None).await?;

            Ok::<(), Error>(())
        })
        .unwrap();

    // The runtime needs to be terminated because the node can currently only be killed when the runtime itself shuts down.

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let provider = setup_test_provider(vec![], vec![], Some(node_config), None).await?;
            // the same wallet that was used when rocksdb was built. When we connect it to the provider, we expect it to have the same amount of assets
            let mut wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            wallet.set_provider(provider.clone());

            let blocks = provider
                .get_blocks(PaginationRequest {
                    cursor: None,
                    results: 10,
                    direction: PageDirection::Forward,
                })
                .await?
                .results;

            assert_eq!(blocks.len(), 3);
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(wallet.get_balances().await?.len(), 2);

            fs::remove_dir_all(
                temp_database_path
                    .parent()
                    .expect("db parent folder does not exist"),
            )?;

            Ok::<(), Error>(())
        })
        .unwrap();
}

#[tokio::test]
async fn can_configure_decoding_of_contract_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: Will not work if max_tokens not big enough
        methods.i_return_a_1k_el_array().with_decoder_config(DecoderConfig{max_tokens: 100, ..Default::default()}).call().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Single call: Works when limit is bumped
        let result = methods
            .i_return_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(result, [0; 1000]);
    }
    {
        // Multi call: Will not work if max_tokens not big enough
        CallHandler::new_multi_call(wallet.clone())
         .add_call(methods.i_return_a_1k_el_array())
         .with_decoder_config(DecoderConfig { max_tokens: 100, ..Default::default() })
         .call::<([u8; 1000],)>().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Multi call: Works when configured
        CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_return_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<([u8; 1000],)>()
            .await
            .unwrap();
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let submitted_tx = contract_methods.get(1, 2).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;

    assert_eq!(value, 3);

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let handle = multi_call_handler.submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let (val_1, val_2): (u64, u64) = handle.response().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_heap_type_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vector_output"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        let call_handler_1 = contract_instance.methods().u8_in_vec(5);
        let call_handler_2 = contract_instance_2.methods().get_single(7);
        let call_handler_3 = contract_instance.methods().u8_in_vec(3);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2)
            .add_call(call_handler_3);

        let (val_1, val_2, val_3): (Vec<u8>, u64, Vec<u8>) = multi_call_handler.call().await?.value;

        assert_eq!(val_1, vec![0, 1, 2, 3, 4]);
        assert_eq!(val_2, 7);
        assert_eq!(val_3, vec![0, 1, 2]);
    }

    Ok(())
}

#[tokio::test]
async fn heap_types_correctly_offset_in_create_transactions_w_storage_slots() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Predicate(
            name = "MyPredicate",
            project = "e2e/sway/types/predicates/predicate_vector"
        ),),
    );

    let provider = wallet.try_provider()?.clone();
    let data = MyPredicateEncoder::default().encode_data(18, 24, vec![2, 4, 42])?;
    let predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(data)
    .with_provider(provider);

    wallet
        .transfer(
            predicate.address(),
            10_000,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await?;

    // if the contract is successfully deployed then the predicate was unlocked. This further means
    // the offsets were setup correctly since the predicate uses heap types in its arguments.
    // Storage slots were loaded automatically by default
    Contract::load_from(
        "sway/contracts/storage/out/release/storage.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    Ok(())
}

#[tokio::test]
async fn test_arguments_with_gas_forwarded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vectors"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let x = 128;
    let vec_input = vec![0, 1, 2];
    {
        let response = contract_instance
            .methods()
            .get_single(x)
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;

        assert_eq!(response.value, x);
    }
    {
        contract_instance_2
            .methods()
            .u32_vec(vec_input.clone())
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;
    }
    {
        let call_handler_1 = contract_instance.methods().get_single(x);
        let call_handler_2 = contract_instance_2.methods().u32_vec(vec_input);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let (value, _): (u64, ()) = multi_call_handler.call().await?.value;

        assert_eq!(value, x);
    }

    Ok(())
}

#[tokio::test]
async fn contract_custom_call_no_signatures_strategy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let provider = wallet.try_provider()?;

    let counter = 42;
    let call_handler = contract_instance.methods().initialize_counter(counter);

    let mut tb = call_handler.transaction_builder().await?;

    let base_asset_id = *provider.consensus_parameters().await?.base_asset_id();

    let amount = 10;
    let consensus_parameters = provider.consensus_parameters().await?;
    let new_base_inputs = wallet
        .get_asset_inputs_for_amount(base_asset_id, amount, None)
        .await?;
    tb.inputs_mut().extend(new_base_inputs);
    tb.outputs_mut()
        .push(Output::change(wallet.address().into(), 0, base_asset_id));

    // ANCHOR: tb_no_signatures_strategy
    let mut tx = tb
        .with_build_strategy(ScriptBuildStrategy::NoSignatures)
        .build(provider)
        .await?;
    // ANCHOR: tx_sign_with
    tx.sign_with(&wallet, consensus_parameters.chain_id())
        .await?;
    // ANCHOR_END: tx_sign_with
    // ANCHOR_END: tb_no_signatures_strategy

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;

    let tx_status = provider.tx_status(&tx_id).await?;

    let response = call_handler.get_response_from(tx_status)?;

    assert_eq!(counter, response.value);

    Ok(())
}

#[tokio::test]
async fn contract_encoder_config_is_applied() -> Result<()> {
    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Wallets("wallet")
    );
    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let instance = TestContract::new(contract_id.clone(), wallet.clone());

    {
        let _encoding_ok = instance
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let instance_with_encoder_config = instance.with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));

        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));
    }

    Ok(())
}

#[tokio::test]
async fn test_reentrant_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LibContractCaller",
            project = "e2e/sway/contracts/lib_contract_caller"
        ),),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = contract_caller_instance.contract_id();
    let response = contract_caller_instance
        .methods()
        .re_entrant(contract_id, true)
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn msg_sender_gas_estimation_issue() {
    // Gas estimation requires an input of the base asset. If absent, a fake input is
    // added. However, if a non-base coin is present and the fake input introduces a
    // second owner, it causes the `msg_sender` sway fn to fail. This leads
    // to a premature failure in gas estimation, risking transaction failure due to
    // a low gas limit.
    let mut wallet = WalletUnlocked::new_random(None);

    let (coins, ids) =
        setup_multiple_assets_coins(wallet.address(), 2, DEFAULT_NUM_COINS, DEFAULT_COIN_AMOUNT);

    let provider = setup_test_provider(coins, vec![], None, None)
        .await
        .unwrap();
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/msg_methods"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let asset_id = ids[0];

    // The fake coin won't be added if we add a base asset, so let's not do that
    assert!(
        asset_id
            != *provider
                .consensus_parameters()
                .await
                .unwrap()
                .base_asset_id()
    );
    let call_params = CallParameters::default()
        .with_amount(100)
        .with_asset_id(asset_id);

    contract_instance
        .methods()
        .message_sender()
        .call_params(call_params)
        .unwrap()
        .call()
        .await
        .unwrap();
}

#[tokio::test]
async fn variable_output_estimation_is_optimized() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/var_outputs"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_methods = contract_instance.methods();

    let coins = 252;
    let recipient = Identity::Address(wallet.address().into());
    let start = Instant::now();
    let _ = contract_methods
        .mint(coins, recipient)
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    // debug builds are slower (20x for `fuel-core-lib`, 4x for a release-fuel-core-binary)
    // we won't validate in that case so we don't have to maintain two expectations
    if !cfg!(debug_assertions) {
        let elapsed = start.elapsed().as_secs();
        let limit = 2;
        if elapsed > limit {
            panic!("Estimation took too long ({elapsed}). Limit is {limit}");
        }
    }

    Ok(())
}

async fn setup_node_with_high_price() -> Result<Vec<WalletUnlocked>> {
    let wallet_config = WalletsConfig::new(None, None, None);
    let fee_parameters = FeeParameters::V1(FeeParametersV1 {
        gas_price_factor: 92000,
        gas_per_byte: 63,
    });
    let consensus_parameters = ConsensusParameters::V1(ConsensusParametersV1 {
        fee_params: fee_parameters,
        ..Default::default()
    });
    let node_config = Some(NodeConfig {
        starting_gas_price: 1100,
        ..NodeConfig::default()
    });
    let chain_config = ChainConfig {
        consensus_parameters,
        ..ChainConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(wallet_config, node_config, Some(chain_config))
            .await?;

    Ok(wallets)
}

#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);

    let response = MyContract::new(contract_id, no_funds_wallet.clone())
        .methods()
        .get(5, 6)
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn simulations_can_be_made_without_coins_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let contract_instance = MyContract::new(contract_id, no_funds_wallet.clone());

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.get(1, 2);
    let call_handler_2 = contract_methods.get(3, 4);

    let mut multi_call_handler = CallHandler::new_multi_call(no_funds_wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let value: (u64, u64) = multi_call_handler
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value, (3, 7));

    Ok(())
}

#[tokio::test]
async fn contract_call_with_non_zero_base_asset_id_and_tip() -> Result<()> {
    use fuels::{prelude::*, tx::ConsensusParameters};

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let asset_id = AssetId::new([1; 32]);

    let mut consensus_parameters = ConsensusParameters::default();
    consensus_parameters.set_base_asset_id(asset_id);

    let config = ChainConfig {
        consensus_parameters,
        ..Default::default()
    };

    let asset_base = AssetConfig {
        id: asset_id,
        num_coins: 1,
        coin_amount: 10_000,
    };

    let wallet_config = WalletsConfig::new_multiple_assets(1, vec![asset_base]);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, Some(config)).await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_tip(10))
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn max_fee_estimation_respects_tolerance() -> Result<()> {
    use fuels::prelude::*;

    let mut call_wallet = WalletUnlocked::new_random(None);

    let call_coins = setup_single_asset_coins(call_wallet.address(), AssetId::BASE, 1000, 1);

    let mut deploy_wallet = WalletUnlocked::new_random(None);
    let deploy_coins =
        setup_single_asset_coins(deploy_wallet.address(), AssetId::BASE, 1, 1_000_000);

    let provider =
        setup_test_provider([call_coins, deploy_coins].concat(), vec![], None, None).await?;

    call_wallet.set_provider(provider.clone());
    deploy_wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            wallet = "deploy_wallet",
            contract = "MyContract",
            random_salt = false,
        )
    );
    let contract_instance = contract_instance.with_account(call_wallet.clone());

    let max_fee_from_tx = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            let builder = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap();

            assert_eq!(
                builder.max_fee_estimation_tolerance, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
                "Expected pre-set tolerance"
            );

            builder
                .with_max_fee_estimation_tolerance(tolerance)
                .build(&provider)
                .await
                .unwrap()
                .max_fee()
                .unwrap()
        }
    };

    let max_fee_from_builder = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance)
                .estimate_max_fee(&provider)
                .await
                .unwrap()
        }
    };

    let base_amount_in_inputs = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let call_wallet = &call_wallet;
        async move {
            let mut tb = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance);

            call_wallet.adjust_for_fee(&mut tb, 0).await.unwrap();
            tb.inputs
                .iter()
                .filter_map(|input: &Input| match input {
                    Input::ResourceSigned { resource }
                        if resource.coin_asset_id().unwrap() == AssetId::BASE =>
                    {
                        Some(resource.amount())
                    }
                    _ => None,
                })
                .sum::<u64>()
        }
    };

    let no_increase_max_fee = max_fee_from_tx(0.0).await;
    let increased_max_fee = max_fee_from_tx(2.00).await;

    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let no_increase_max_fee = max_fee_from_builder(0.0).await;
    let increased_max_fee = max_fee_from_builder(2.00).await;
    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let normal_base_asset = base_amount_in_inputs(0.0).await;
    let more_base_asset_due_to_bigger_tolerance = base_amount_in_inputs(5.00).await;
    assert!(more_base_asset_due_to_bigger_tolerance > normal_base_asset);

    Ok(())
}

#[tokio::test]
async fn blob_contract_deployment() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
    ));

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract_size = std::fs::metadata(contract_binary)
        .expect("contract file not found")
        .len();

    assert!(
         contract_size > 150_000,
         "the testnet size limit was around 100kB, we want a contract bigger than that to reflect prod (current: {contract_size}B)"
     );

    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::new(Some(2), None, None), None, None)
            .await?;

    let provider = wallets[0].provider().unwrap().clone();

    let consensus_parameters = provider.consensus_parameters().await?;

    let contract_max_size = consensus_parameters.contract_params().contract_max_size();
    assert!(
         contract_size > contract_max_size,
         "this test should ideally be run with a contract bigger than the max contract size ({contract_max_size}B) so that we know deployment couldn't have happened without blobs"
     );

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallets[0], TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn regular_contract_can_be_deployed() -> Result<()> {
    // given
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
    );

    let contract_binary = "sway/contracts/contract_test/out/release/contract_test.bin";

    // when
    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    // then
    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance
        .methods()
        .get_counter()
        .call()
        .await?
        .value;

    assert_eq!(response, 0);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_be_deployed_directly() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_upload_blobs_separately_then_deploy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    let blob_ids = contract.blob_ids();

    // if this were an example for the user we'd just call `deploy` on the contract above
    // this way we are testing that the blobs were really deployed above, otherwise the following
    // would fail
    let contract_id = Contract::loader_from_blob_ids(
        blob_ids.to_vec(),
        contract.salt(),
        contract.storage_slots().to_vec(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_blob_already_uploaded_not_an_issue() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?;

    // this will upload blobs
    contract
        .clone()
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    // this will try to upload the blobs but skip upon encountering an error
    let contract_id = contract
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .something()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_storage_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_storage_slots = contract.storage_slots().to_vec();

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";
    let proxy_contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let combined_storage_slots = [&contract_storage_slots, proxy_contract.storage_slots()].concat();

    let proxy_id = proxy_contract
        .with_storage_slots(combined_storage_slots)
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?
        .value;

    assert_eq!(response, 42);

    let _res = proxy
        .methods()
        .write_some_u64(36)
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 36);

    Ok(())
}\n```

Note: forwarding gas to a contract call is always possible, regardless of the contract method being non-payable.

You can also use CallParameters::default() to use the default values:

```rust\nuse fuel_tx::Word;

pub const ENUM_DISCRIMINANT_BYTE_WIDTH: usize = 8;
pub const WORD_SIZE: usize = core::mem::size_of::<Word>();

// ANCHOR: default_call_parameters
pub const DEFAULT_CALL_PARAMS_AMOUNT: u64 = 0;
// ANCHOR_END: default_call_parameters

pub const DEFAULT_GAS_ESTIMATION_TOLERANCE: f64 = 0.2;
pub const DEFAULT_GAS_ESTIMATION_BLOCK_HORIZON: u32 = 5;

// The size of a signature inside a transaction `Witness`
pub const WITNESS_STATIC_SIZE: usize = 8;
const SIGNATURE_SIZE: usize = 64;
pub const SIGNATURE_WITNESS_SIZE: usize = WITNESS_STATIC_SIZE + SIGNATURE_SIZE;\n```

This way:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

The gas_forwarded parameter defines the limit for the actual contract call as opposed to the gas limit for the whole transaction. This means that it is constrained by the transaction limit. If it is set to an amount greater than the available gas, all available gas will be forwarded.

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

If you don't set the call parameters or use CallParameters::default(), the transaction gas limit will be forwarded instead.

Custom asset transfer

The SDK provides the option to transfer assets within the same transaction, when making a contract call. By using add_custom_asset() you specify the asset ID, the amount, and the destination address:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Call response

You've probably noticed that you're often chaining .call().await.unwrap(). That's because:

  1. You have to choose between .call() and .simulate() (more on this in the next section).
  2. Contract calls are asynchronous, so you can choose to either .await it or perform concurrent tasks, making full use of Rust's async.
  3. .unwrap() the Result<CallResponse, Error> returned by the contract call.

Once you unwrap the CallResponse, you have access to this struct:

```rust\nuse std::fmt::Debug;

use fuel_tx::{Bytes32, Receipt};
use fuels_core::{
    codec::{LogDecoder, LogResult},
    traits::{Parameterize, Tokenizable},
    types::errors::Result,
};

/// [`CallResponse`] is a struct that is returned by a call to the contract or script. Its value
/// field holds the decoded typed value returned by the contract's method. The other field holds all
/// the receipts returned by the call.
#[derive(Debug)]
// ANCHOR: call_response
pub struct CallResponse<D> {
    pub value: D,
    pub receipts: Vec<Receipt>,
    pub gas_used: u64,
    pub log_decoder: LogDecoder,
    pub tx_id: Option<Bytes32>,
}
// ANCHOR_END: call_response

impl<D> CallResponse<D> {
    /// Get the gas used from ScriptResult receipt
    fn get_gas_used(receipts: &[Receipt]) -> u64 {
        receipts
            .iter()
            .rfind(|r| matches!(r, Receipt::ScriptResult { .. }))
            .expect("could not retrieve ScriptResult")
            .gas_used()
            .expect("could not retrieve gas used from ScriptResult")
    }

    pub fn new(
        value: D,
        receipts: Vec<Receipt>,
        log_decoder: LogDecoder,
        tx_id: Option<Bytes32>,
    ) -> Self {
        Self {
            value,
            gas_used: Self::get_gas_used(&receipts),
            receipts,
            log_decoder,
            tx_id,
        }
    }

    pub fn decode_logs(&self) -> LogResult {
        self.log_decoder.decode_logs(&self.receipts)
    }

    pub fn decode_logs_with_type<T: Tokenizable + Parameterize + 'static>(&self) -> Result<Vec<T>> {
        self.log_decoder.decode_logs_with_type::<T>(&self.receipts)
    }
}\n```

Where value will hold the value returned by its respective contract method, represented by the exact type returned by the FuelVM, E.g., if your contract returns a FuelVM's u64, value's D will be a u64. If it's a FuelVM's tuple (u8,bool), then D will be a (u8,bool). If it's a custom type, for instance, a Sway struct MyStruct containing two components, a u64, and a b256, D will be a struct generated at compile-time, called MyStruct with u64 and a [u8; 32] (the equivalent of b256 in Rust).

  • receipts will hold all receipts generated by that specific contract call.
  • gas_used is the amount of gas consumed by the contract call.
  • tx_id will hold the ID of the corresponding submitted transaction.

Error handling

You can use the is_ok and is_err methods to check if a contract call Result is Ok or contains an error. These methods will return either true or false.

let is_ok = response.is_ok();
let is_error = response.is_err();

If is_err returns true, you can use the unwrap_err method to unwrap the error message.

if response.is_err() {
    let err = response.unwrap_err();
    println!("ERROR: {:?}", err);
};

Logs

Whenever you log a value within a contract method, the resulting log entry is added to the log receipt and the variable type is recorded in the contract's ABI. The SDK lets you parse those values into Rust types.

Consider the following contract method:

```sway\ncontract;

use std::{logging::log, string::String};
use contract_logs_abi::ContractLogs;

#[allow(dead_code)]
struct TestStruct {
    field_1: bool,
    field_2: b256,
    field_3: u64,
}

#[allow(dead_code)]
enum TestEnum {
    VariantOne: (),
    VariantTwo: (),
}

#[allow(dead_code)]
struct StructWithGeneric<D> {
    field_1: D,
    field_2: u64,
}

#[allow(dead_code)]
enum EnumWithGeneric<D> {
    VariantOne: D,
    VariantTwo: (),
}

#[allow(dead_code)]
struct StructWithNestedGeneric<D> {
    field_1: D,
    field_2: u64,
}

#[allow(dead_code)]
struct StructDeeplyNestedGeneric<D> {
    field_1: D,
    field_2: u64,
}

impl ContractLogs for Contract {
    fn produce_logs_values() {
        log(64u64);
        log(32u32);
        log(16u16);
        log(8u8);
    }

    // ANCHOR: produce_logs
    fn produce_logs_variables() {
        let f: u64 = 64;
        let u: b256 = 0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a;
        let e: str[4] = __to_str_array("Fuel");
        let l: [u8; 3] = [1u8, 2u8, 3u8];

        log(f);
        log(u);
        log(e);
        log(l);
    }
    // ANCHOR_END: produce_logs
    fn produce_logs_custom_types() {
        let f: u64 = 64;
        let u: b256 = 0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a;

        let test_struct = TestStruct {
            field_1: true,
            field_2: u,
            field_3: f,
        };
        let test_enum = TestEnum::VariantTwo;

        log(test_struct);
        log(test_enum);
        log((test_struct, test_enum));
    }

    fn produce_logs_generic_types() {
        let l: [u8; 3] = [1u8, 2u8, 3u8];

        let test_struct = StructWithGeneric {
            field_1: l,
            field_2: 64,
        };
        let test_enum = EnumWithGeneric::VariantOne(l);
        let test_struct_nested = StructWithNestedGeneric {
            field_1: test_struct,
            field_2: 64,
        };
        let test_deeply_nested_generic = StructDeeplyNestedGeneric {
            field_1: test_struct_nested,
            field_2: 64,
        };

        log(test_struct);
        log(test_enum);
        log(test_struct_nested);
        log(test_deeply_nested_generic);
    }

    fn produce_multiple_logs() {
        let f: u64 = 64;
        let u: b256 = 0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a;
        let e: str[4] = __to_str_array("Fuel");
        let l: [u8; 3] = [1u8, 2u8, 3u8];
        let test_struct = TestStruct {
            field_1: true,
            field_2: u,
            field_3: f,
        };
        let test_enum = TestEnum::VariantTwo;
        let test_generic_struct = StructWithGeneric {
            field_1: test_struct,
            field_2: 64,
        };

        log(64);
        log(32u32);
        log(16u16);
        log(8u8);
        log(f);
        log(u);
        log(e);
        log(l);
        log(test_struct);
        log(test_enum);
        log(test_generic_struct);
    }

    fn produce_bad_logs() {
        // produce a custom log with log id 128
        // this log id will not be present in abi JSON
        asm(r1: 0, r2: 128, r3: 0, r4: 0) {
            log r1 r2 r3 r4;
        }

        log(123);
    }

    fn produce_string_slice_log() {
        log("fuel");
    }

    fn produce_string_log() {
        log(String::from_ascii_str("fuel"));
    }

    fn produce_bytes_log() {
        log(String::from_ascii_str("fuel").as_bytes());
    }

    fn produce_raw_slice_log() {
        log(String::from_ascii_str("fuel").as_raw_slice());
    }

    fn produce_vec_log() {
        let mut v = Vec::new();
        v.push(1u16);
        v.push(2u16);
        v.push(3u16);

        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;

        let mut v1 = Vec::new();
        v1.push(some_enum);
        v1.push(other_enum);
        v1.push(some_enum);

        let mut v2 = Vec::new();
        v2.push(v1);
        v2.push(v1);

        let mut v3 = Vec::new();
        v3.push(v2);

        log(v3);
    }
}\n```

You can access the logged values in Rust by calling decode_logs_with_type::<T> from a CallResponse, where T is the type of the logged variables you want to retrieve. The result will be a Vec<T>:

```rust\nuse fuels::{
    core::codec::DecoderConfig,
    prelude::*,
    types::{errors::transaction::Reason, AsciiString, Bits256, SizedAsciiString},
};

#[tokio::test]
async fn test_parse_logged_variables() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: produce_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_variables().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_bits256 = response.decode_logs_with_type::<Bits256>()?;
    let log_string = response.decode_logs_with_type::<SizedAsciiString<4>>()?;
    let log_array = response.decode_logs_with_type::<[u8; 3]>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_bits256, vec![expected_bits256]);
    assert_eq!(log_string, vec!["Fuel"]);
    assert_eq!(log_array, vec![[1, 2, 3]]);
    // ANCHOR_END: produce_logs

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_values() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_values().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_custom_types().call().await?;

    let log_test_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_test_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_tuple = response.decode_logs_with_type::<(TestStruct, TestEnum)>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;

    assert_eq!(log_test_struct, vec![expected_struct.clone()]);
    assert_eq!(log_test_enum, vec![expected_enum.clone()]);
    assert_eq!(log_tuple, vec![(expected_struct, expected_enum)]);

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_generic_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_generic_types().call().await?;

    let log_struct = response.decode_logs_with_type::<StructWithGeneric<[_; 3]>>()?;
    let log_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_struct_nested =
        response.decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<[_; 3]>>>()?;
    let log_struct_deeply_nested = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<[_; 3]>>,
    >>()?;

    let l = [1u8, 2u8, 3u8];
    let expected_struct = StructWithGeneric {
        field_1: l,
        field_2: 64,
    };
    let expected_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_struct_nested, vec![expected_nested_struct]);
    assert_eq!(
        log_struct_deeply_nested,
        vec![expected_deeply_nested_struct]
    );

    Ok(())
}

#[tokio::test]
async fn test_decode_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: decode_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_multiple_logs().call().await?;
    let logs = response.decode_logs();
    // ANCHOR_END: decode_logs

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_generic_struct:?}"),
    ];

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
async fn test_decode_logs_with_no_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let logs = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .decode_logs();

    assert!(logs.filter_succeeded().is_empty());

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.produce_logs_values();
    let call_handler_2 = contract_methods.produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_multiple_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_handler_1 = contract_instance.methods().produce_logs_values();
    let call_handler_2 = contract_instance2.methods().produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs"),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id.clone())
        .with_contracts(&[&contract_instance]);

    let call_handler_2 = contract_caller_instance2
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

fn assert_revert_containing_msg(msg: &str, error: Error) {
    assert!(matches!(error, Error::Transaction(Reason::Reverted { .. })));
    if let Error::Transaction(Reason::Reverted { reason, .. }) = error {
        assert!(
            reason.contains(msg),
            "message: \"{msg}\" not contained in reason: \"{reason}\""
        );
    }
}

#[tokio::test]
async fn test_revert_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        ($method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .call()
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
        ($method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(require_primitive, call, "42");
        reverts_with_msg!(require_primitive, simulate, "42");

        reverts_with_msg!(require_string, call, "fuel");
        reverts_with_msg!(require_string, simulate, "fuel");

        reverts_with_msg!(require_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            require_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(require_with_additional_logs, call, "64");
        reverts_with_msg!(require_with_additional_logs, simulate, "64");
    }
    {
        reverts_with_msg!(rev_w_log_primitive, call, "42");
        reverts_with_msg!(rev_w_log_primitive, simulate, "42");

        reverts_with_msg!(rev_w_log_string, call, "fuel");
        reverts_with_msg!(rev_w_log_string, simulate, "fuel");

        reverts_with_msg!(rev_w_log_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            rev_w_log_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_multi_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let contract_methods2 = contract_instance2.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods2.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods2.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_decode_logs() -> Result<()> {
    // ANCHOR: script_logs
    abigen!(Script(
        name = "LogScript",
        abi = "e2e/sway/logs/script_logs/out/release/script_logs-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/logs/script_logs/out/release/script_logs.bin";
    let instance = LogScript::new(wallet.clone(), bin_path);

    let response = instance.main().call().await?;

    let logs = response.decode_logs();
    let log_u64 = response.decode_logs_with_type::<u64>()?;
    // ANCHOR_END: script_logs

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_tuple = (expected_struct.clone(), expected_enum.clone());
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 128u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_tuple:?}"),
        format!("{expected_generic_struct:?}"),
        format!("{expected_generic_enum:?}"),
        format!("{expected_nested_struct:?}"),
        format!("{expected_deeply_nested_struct:?}"),
    ];

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?
        .decode_logs();

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_logs_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Script(
                name = "LogScript",
                project = "e2e/sway/logs/script_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let expected_num_contract_logs = 4;

    let expected_script_logs: Vec<String> = vec![
        // Contract logs
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        // Script logs
        format!("{:?}", true),
        format!("{:?}", 42),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    // ANCHOR: instance_to_contract_id
    let contract_id: ContractId = contract_instance.id().into();
    // ANCHOR_END: instance_to_contract_id

    // ANCHOR: external_contract_ids
    let response = script_instance
        .main(contract_id)
        .with_contract_ids(&[contract_id.into()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    // ANCHOR: external_contract
    let response = script_instance
        .main(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    {
        let num_contract_logs = response
            .receipts
            .iter()
            .filter(|receipt| matches!(receipt, Receipt::LogData { id, .. } | Receipt::Log { id, .. } if *id == contract_id))
            .count();

        assert_eq!(num_contract_logs, expected_num_contract_logs);
    }
    {
        let logs = response.decode_logs();

        assert_eq!(logs.filter_succeeded(), expected_script_logs);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_decode_logs_with_type() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let response = script_instance.main().call().await?;

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    let log_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_generic_struct = response.decode_logs_with_type::<StructWithGeneric<TestStruct>>()?;
    let log_generic_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_nested_struct = response
        .decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<TestStruct>>>()?;
    let log_deeply_nested_struct = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<TestStruct>>,
    >>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![128, 64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_generic_struct, vec![expected_generic_struct]);
    assert_eq!(log_generic_enum, vec![expected_generic_enum]);
    assert_eq!(log_nested_struct, vec![expected_nested_struct]);
    assert_eq!(
        log_deeply_nested_struct,
        vec![expected_deeply_nested_struct]
    );
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_script_require_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_revert_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(MatchEnum::RequirePrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RequirePrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RequireString, call, "fuel");
        reverts_with_msg!(MatchEnum::RequireString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, call, "64");
        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, simulate, "64");
    }
    {
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RevWLogString, call, "fuel");
        reverts_with_msg!(MatchEnum::RevWLogString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let error = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractLogs",
                project = "e2e/sway/logs/contract_logs",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "ContractLogs",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let lib_contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_instance.methods().produce_logs_values();

    let call_handler_2 = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&lib_contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let error = multi_call_handler
        .call::<((), ())>()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_script_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_loader_script_require_from_loader_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let contract_binary = "sway/contracts/lib_contract/out/release/lib_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;
    let contract_instance = MyContract::new(contract_id, wallet);

    let mut script_instance = script_instance;
    script_instance.convert_into_loader().await?;

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

fn assert_assert_eq_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left == right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

fn assert_assert_ne_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left != right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

#[tokio::test]
async fn test_contract_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/contracts/asserts"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        (($($arg: expr,)*), $method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        (($($arg: expr,)*), $method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!((32, 64,), assert_primitive, call, "assertion failed");
        reverts_with_msg!((32, 64,), assert_primitive, simulate, "assertion failed");
    }

    macro_rules! reverts_with_assert_eq_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_eq_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, call, "assertion failed");
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;
        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            simulate,
            "assertion failed"
        );
    }

    macro_rules! reverts_with_assert_ne_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_ne_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, call, "assertion failed");
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            simulate,
            "assertion failed"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_script_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_asserts"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    macro_rules! reverts_with_assert_eq_ne_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            call,
            "assertion failed"
        );
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            simulate,
            "assertion failed"
        );
    }
    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }

    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }

    Ok(())
}

#[tokio::test]
async fn contract_token_ops_error_messages() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/token_ops"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let contract_id = contract_instance.contract_id();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());
        let address = wallet.address();

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .simulate(Execution::Realistic)
            .await
            .expect_err("should return a revert error");
        assert_revert_containing_msg("failed transfer to address", error);

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .call()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("failed transfer to address", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_log_results() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let response = contract_instance
        .methods()
        .produce_bad_logs()
        .call()
        .await?;

    let log = response.decode_logs();

    let expected_err = format!(
        "codec: missing log formatter for log_id: `LogId({:?}, \"128\")`, data: `{:?}`. \
         Consider adding external contracts using `with_contracts()`",
        contract_instance.id().hash,
        [0u8; 8]
    );

    let succeeded = log.filter_succeeded();
    let failed = log.filter_failed();
    assert_eq!(succeeded, vec!["123".to_string()]);
    assert_eq!(failed.first().unwrap().to_string(), expected_err);

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_contract_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: decoding with too low max_tokens fails
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "Should have failed since there are more tokens than what is supported by default.",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "Should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Single call: increasing limits makes the test pass
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }
    {
        // Multi call: decoding with too low max_tokens will fail
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "should have failed since there are more tokens than what is supported by default",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Multi call: increasing limits makes the test pass
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_script_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_needs_custom_decoder_logging"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    {
        // Cannot decode the produced log with too low max_tokens
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response
            .decode_logs_with_type::<[u8; 1000]>()
            .expect_err("Cannot decode the log with default decoder config");

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty())
    }
    {
        // When the token limit is bumped log decoding succeeds
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty())
    }

    Ok(())
}

#[tokio::test]
async fn contract_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.produce_string_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let response = contract_methods.produce_string_log().call().await?;
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let response = contract_methods.produce_bytes_log().call().await?;
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let response = contract_methods.produce_raw_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let response = contract_methods.produce_vec_log().call().await?;
        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}

#[tokio::test]
async fn script_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_heap_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    let response = script_instance.main().call().await?;

    {
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}\n```

You can use the decode_logs() function to retrieve a LogResult struct containing a results field that is a vector of Result<String> values representing the success or failure of decoding each log.

```rust\nuse fuels::{
    core::codec::DecoderConfig,
    prelude::*,
    types::{errors::transaction::Reason, AsciiString, Bits256, SizedAsciiString},
};

#[tokio::test]
async fn test_parse_logged_variables() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: produce_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_variables().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_bits256 = response.decode_logs_with_type::<Bits256>()?;
    let log_string = response.decode_logs_with_type::<SizedAsciiString<4>>()?;
    let log_array = response.decode_logs_with_type::<[u8; 3]>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_bits256, vec![expected_bits256]);
    assert_eq!(log_string, vec!["Fuel"]);
    assert_eq!(log_array, vec![[1, 2, 3]]);
    // ANCHOR_END: produce_logs

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_values() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_values().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_custom_types().call().await?;

    let log_test_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_test_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_tuple = response.decode_logs_with_type::<(TestStruct, TestEnum)>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;

    assert_eq!(log_test_struct, vec![expected_struct.clone()]);
    assert_eq!(log_test_enum, vec![expected_enum.clone()]);
    assert_eq!(log_tuple, vec![(expected_struct, expected_enum)]);

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_generic_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_generic_types().call().await?;

    let log_struct = response.decode_logs_with_type::<StructWithGeneric<[_; 3]>>()?;
    let log_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_struct_nested =
        response.decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<[_; 3]>>>()?;
    let log_struct_deeply_nested = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<[_; 3]>>,
    >>()?;

    let l = [1u8, 2u8, 3u8];
    let expected_struct = StructWithGeneric {
        field_1: l,
        field_2: 64,
    };
    let expected_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_struct_nested, vec![expected_nested_struct]);
    assert_eq!(
        log_struct_deeply_nested,
        vec![expected_deeply_nested_struct]
    );

    Ok(())
}

#[tokio::test]
async fn test_decode_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: decode_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_multiple_logs().call().await?;
    let logs = response.decode_logs();
    // ANCHOR_END: decode_logs

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_generic_struct:?}"),
    ];

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
async fn test_decode_logs_with_no_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let logs = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .decode_logs();

    assert!(logs.filter_succeeded().is_empty());

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.produce_logs_values();
    let call_handler_2 = contract_methods.produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_multiple_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_handler_1 = contract_instance.methods().produce_logs_values();
    let call_handler_2 = contract_instance2.methods().produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs"),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id.clone())
        .with_contracts(&[&contract_instance]);

    let call_handler_2 = contract_caller_instance2
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

fn assert_revert_containing_msg(msg: &str, error: Error) {
    assert!(matches!(error, Error::Transaction(Reason::Reverted { .. })));
    if let Error::Transaction(Reason::Reverted { reason, .. }) = error {
        assert!(
            reason.contains(msg),
            "message: \"{msg}\" not contained in reason: \"{reason}\""
        );
    }
}

#[tokio::test]
async fn test_revert_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        ($method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .call()
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
        ($method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(require_primitive, call, "42");
        reverts_with_msg!(require_primitive, simulate, "42");

        reverts_with_msg!(require_string, call, "fuel");
        reverts_with_msg!(require_string, simulate, "fuel");

        reverts_with_msg!(require_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            require_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(require_with_additional_logs, call, "64");
        reverts_with_msg!(require_with_additional_logs, simulate, "64");
    }
    {
        reverts_with_msg!(rev_w_log_primitive, call, "42");
        reverts_with_msg!(rev_w_log_primitive, simulate, "42");

        reverts_with_msg!(rev_w_log_string, call, "fuel");
        reverts_with_msg!(rev_w_log_string, simulate, "fuel");

        reverts_with_msg!(rev_w_log_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            rev_w_log_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_multi_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let contract_methods2 = contract_instance2.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods2.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods2.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_decode_logs() -> Result<()> {
    // ANCHOR: script_logs
    abigen!(Script(
        name = "LogScript",
        abi = "e2e/sway/logs/script_logs/out/release/script_logs-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/logs/script_logs/out/release/script_logs.bin";
    let instance = LogScript::new(wallet.clone(), bin_path);

    let response = instance.main().call().await?;

    let logs = response.decode_logs();
    let log_u64 = response.decode_logs_with_type::<u64>()?;
    // ANCHOR_END: script_logs

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_tuple = (expected_struct.clone(), expected_enum.clone());
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 128u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_tuple:?}"),
        format!("{expected_generic_struct:?}"),
        format!("{expected_generic_enum:?}"),
        format!("{expected_nested_struct:?}"),
        format!("{expected_deeply_nested_struct:?}"),
    ];

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?
        .decode_logs();

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_logs_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Script(
                name = "LogScript",
                project = "e2e/sway/logs/script_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let expected_num_contract_logs = 4;

    let expected_script_logs: Vec<String> = vec![
        // Contract logs
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        // Script logs
        format!("{:?}", true),
        format!("{:?}", 42),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    // ANCHOR: instance_to_contract_id
    let contract_id: ContractId = contract_instance.id().into();
    // ANCHOR_END: instance_to_contract_id

    // ANCHOR: external_contract_ids
    let response = script_instance
        .main(contract_id)
        .with_contract_ids(&[contract_id.into()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    // ANCHOR: external_contract
    let response = script_instance
        .main(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    {
        let num_contract_logs = response
            .receipts
            .iter()
            .filter(|receipt| matches!(receipt, Receipt::LogData { id, .. } | Receipt::Log { id, .. } if *id == contract_id))
            .count();

        assert_eq!(num_contract_logs, expected_num_contract_logs);
    }
    {
        let logs = response.decode_logs();

        assert_eq!(logs.filter_succeeded(), expected_script_logs);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_decode_logs_with_type() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let response = script_instance.main().call().await?;

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    let log_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_generic_struct = response.decode_logs_with_type::<StructWithGeneric<TestStruct>>()?;
    let log_generic_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_nested_struct = response
        .decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<TestStruct>>>()?;
    let log_deeply_nested_struct = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<TestStruct>>,
    >>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![128, 64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_generic_struct, vec![expected_generic_struct]);
    assert_eq!(log_generic_enum, vec![expected_generic_enum]);
    assert_eq!(log_nested_struct, vec![expected_nested_struct]);
    assert_eq!(
        log_deeply_nested_struct,
        vec![expected_deeply_nested_struct]
    );
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_script_require_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_revert_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(MatchEnum::RequirePrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RequirePrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RequireString, call, "fuel");
        reverts_with_msg!(MatchEnum::RequireString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, call, "64");
        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, simulate, "64");
    }
    {
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RevWLogString, call, "fuel");
        reverts_with_msg!(MatchEnum::RevWLogString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let error = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractLogs",
                project = "e2e/sway/logs/contract_logs",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "ContractLogs",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let lib_contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_instance.methods().produce_logs_values();

    let call_handler_2 = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&lib_contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let error = multi_call_handler
        .call::<((), ())>()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_script_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_loader_script_require_from_loader_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let contract_binary = "sway/contracts/lib_contract/out/release/lib_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;
    let contract_instance = MyContract::new(contract_id, wallet);

    let mut script_instance = script_instance;
    script_instance.convert_into_loader().await?;

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

fn assert_assert_eq_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left == right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

fn assert_assert_ne_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left != right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

#[tokio::test]
async fn test_contract_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/contracts/asserts"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        (($($arg: expr,)*), $method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        (($($arg: expr,)*), $method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!((32, 64,), assert_primitive, call, "assertion failed");
        reverts_with_msg!((32, 64,), assert_primitive, simulate, "assertion failed");
    }

    macro_rules! reverts_with_assert_eq_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_eq_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, call, "assertion failed");
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;
        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            simulate,
            "assertion failed"
        );
    }

    macro_rules! reverts_with_assert_ne_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_ne_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, call, "assertion failed");
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            simulate,
            "assertion failed"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_script_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_asserts"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    macro_rules! reverts_with_assert_eq_ne_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            call,
            "assertion failed"
        );
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            simulate,
            "assertion failed"
        );
    }
    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }

    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }

    Ok(())
}

#[tokio::test]
async fn contract_token_ops_error_messages() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/token_ops"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let contract_id = contract_instance.contract_id();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());
        let address = wallet.address();

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .simulate(Execution::Realistic)
            .await
            .expect_err("should return a revert error");
        assert_revert_containing_msg("failed transfer to address", error);

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .call()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("failed transfer to address", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_log_results() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let response = contract_instance
        .methods()
        .produce_bad_logs()
        .call()
        .await?;

    let log = response.decode_logs();

    let expected_err = format!(
        "codec: missing log formatter for log_id: `LogId({:?}, \"128\")`, data: `{:?}`. \
         Consider adding external contracts using `with_contracts()`",
        contract_instance.id().hash,
        [0u8; 8]
    );

    let succeeded = log.filter_succeeded();
    let failed = log.filter_failed();
    assert_eq!(succeeded, vec!["123".to_string()]);
    assert_eq!(failed.first().unwrap().to_string(), expected_err);

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_contract_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: decoding with too low max_tokens fails
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "Should have failed since there are more tokens than what is supported by default.",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "Should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Single call: increasing limits makes the test pass
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }
    {
        // Multi call: decoding with too low max_tokens will fail
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "should have failed since there are more tokens than what is supported by default",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Multi call: increasing limits makes the test pass
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_script_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_needs_custom_decoder_logging"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    {
        // Cannot decode the produced log with too low max_tokens
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response
            .decode_logs_with_type::<[u8; 1000]>()
            .expect_err("Cannot decode the log with default decoder config");

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty())
    }
    {
        // When the token limit is bumped log decoding succeeds
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty())
    }

    Ok(())
}

#[tokio::test]
async fn contract_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.produce_string_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let response = contract_methods.produce_string_log().call().await?;
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let response = contract_methods.produce_bytes_log().call().await?;
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let response = contract_methods.produce_raw_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let response = contract_methods.produce_vec_log().call().await?;
        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}

#[tokio::test]
async fn script_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_heap_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    let response = script_instance.main().call().await?;

    {
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}\n```

Due to possible performance hits, it is not recommended to use decode_logs() outside of a debugging scenario.

Note: String slices cannot be logged directly. Use the __to_str_array() function to convert it to a str[N] first.

Output variables

Sometimes, the contract you call might transfer funds to a specific address, depending on its execution. The underlying transaction for such a contract call has to have the appropriate number of variable outputs to succeed.

Let's say you deployed a contract with the following method:

```sway\ncontract;

use std::{
    asset::*,
    bytes::Bytes,
    constants::ZERO_B256,
    context::balance_of,
    context::msg_amount,
    message::send_message,
};

abi TestFuelCoin {
    fn mint_coins(mint_amount: u64);
    fn mint_to_addresses(mint_amount: u64, addresses: [Identity; 3]);
    fn burn_coins(burn_amount: u64);
    fn transfer(coins: u64, asset_id: AssetId, target: Identity);
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64;
    #[payable]
    fn get_msg_amount() -> u64;
    fn send_message(recipient: b256, coins: u64);
}

impl TestFuelCoin for Contract {
    fn mint_coins(mint_amount: u64) {
        mint(ZERO_B256, mint_amount);
    }
    fn mint_to_addresses(mint_amount: u64, addresses: [Identity; 3]) {
        let mut counter = 0;
        while counter < 3 {
            mint_to(addresses[counter], ZERO_B256, mint_amount);
            counter = counter + 1;
        }
    }

    fn burn_coins(burn_amount: u64) {
        burn(ZERO_B256, burn_amount);
    }

    // ANCHOR: variable_outputs
    fn transfer(coins: u64, asset_id: AssetId, recipient: Identity) {
        transfer(recipient, asset_id, coins);
    }
    // ANCHOR_END: variable_outputs
    fn get_balance(target: ContractId, asset_id: AssetId) -> u64 {
        balance_of(target, asset_id)
    }

    #[payable]
    fn get_msg_amount() -> u64 {
        msg_amount()
    }

    fn send_message(recipient: b256, coins: u64) {
        let mut data = Bytes::new();
        data.push(1u8);
        data.push(2u8);
        data.push(3u8);

        send_message(recipient, data, coins);
    }
}\n```

When calling transfer_coins_to_output with the SDK, you can specify the number of variable outputs:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

with_variable_output_policy sets the policy regarding variable outputs. You can either set the number of variable outputs yourself by providing VariableOutputPolicy::Exactly(n) or let the SDK estimate it for you with VariableOutputPolicy::EstimateMinimum. A variable output indicates that the amount and the owner may vary based on transaction execution.

Note: that the Sway lib-std function mint_to_address calls transfer_to_address under the hood, so you need to call with_variable_output_policy in the Rust SDK tests like you would for transfer_to_address.

Simulating calls

Sometimes you want to simulate a call to a contract without changing the state of the blockchain. This can be achieved by calling .simulate instead of .call and passing in the desired execution context:

  • .simulate(Execution::Realistic) simulates the transaction in a manner that closely resembles a real call. You need a wallet with base assets to cover the transaction cost, even though no funds will be consumed. This is useful for validating that a real call would succeed if made at that moment. It allows you to debug issues with your contract without spending gas.
```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```
  • .simulate(Execution::StateReadOnly) disables many validations, adds fake gas, extra variable outputs, blank witnesses, etc., enabling you to read state even with an account that has no funds.
```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Calling other contracts

If your contract method is calling other contracts you will have to add the appropriate Inputs and Outputs to your transaction. For your convenience, the CallHandler provides methods that prepare those inputs and outputs for you. You have two methods that you can use: with_contracts(&[&contract_instance, ...]) and with_contract_ids(&[&contract_id, ...]).

with_contracts(&[&contract_instance, ...]) requires contract instances that were created using the abigen macro. When setting the external contracts with this method, logs and require revert errors originating from the external contract can be propagated and decoded by the calling contract.

```rust\nuse std::time::Duration;

use fuel_tx::{
    consensus_parameters::{ConsensusParametersV1, FeeParametersV1},
    ConsensusParameters, FeeParameters, Output,
};
use fuels::{
    core::codec::{calldata, encode_fn_selector, DecoderConfig, EncoderConfig},
    prelude::*,
    programs::DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
    tx::ContractParameters,
    types::{errors::transaction::Reason, input::Input, Bits256, Identity},
};
use tokio::time::Instant;

#[tokio::test]
async fn test_multiple_args() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Make sure we can call the contract with multiple arguments
    let contract_methods = contract_instance.methods();
    let response = contract_methods.get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    let t = MyType { x: 5, y: 6 };
    let response = contract_methods.get_alt(t.clone()).call().await?;
    assert_eq!(response.value, t);

    let response = contract_methods.get_single(5).call().await?;
    assert_eq!(response.value, 5);
    Ok(())
}

#[tokio::test]
async fn test_contract_calling_contract() -> Result<()> {
    // Tests a contract call that calls another contract (FooCaller calls FooContract underneath)
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "lib_contract_instance2",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();
    let lib_contract_id2 = lib_contract_instance2.contract_id();

    // Call the contract directly. It increments the given value.
    let response = lib_contract_instance.methods().increment(42).call().await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance
        .methods()
        .increment_from_contracts(lib_contract_id, lib_contract_id2, 42)
        // Note that the two lib_contract_instances have different types
        .with_contracts(&[&lib_contract_instance, &lib_contract_instance2])
        .call()
        .await?;

    assert_eq!(86, response.value);

    // ANCHOR: external_contract
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    assert_eq!(43, response.value);

    // ANCHOR: external_contract_ids
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contract_ids(&[lib_contract_id.clone()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    assert_eq!(43, response.value);
    Ok(())
}

#[tokio::test]
async fn test_reverting_transaction() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertContract",
            project = "e2e/sway/contracts/revert_transaction_error"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .make_transaction_fail(true)
        .call()
        .await;

    assert!(matches!(
        response,
        Err(Error::Transaction(Reason::Reverted { revert_id, .. })) if revert_id == 128
    ));

    Ok(())
}

#[tokio::test]
async fn test_multiple_read_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MultiReadContract",
            project = "e2e/sway/contracts/multiple_read_calls"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MultiReadContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    contract_methods.store(42).call().await?;

    // Use "simulate" because the methods don't actually
    // run a transaction, but just a dry-run
    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);

    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_multi_call_beginner() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let (val_1, val_2): (u64, u64) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_pro() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let my_type_1 = MyType { x: 1, y: 2 };
    let my_type_2 = MyType { x: 3, y: 4 };

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(5);
    let call_handler_2 = contract_methods.get_single(6);
    let call_handler_3 = contract_methods.get_alt(my_type_1.clone());
    let call_handler_4 = contract_methods.get_alt(my_type_2.clone());
    let call_handler_5 = contract_methods.get_array([7; 2]);
    let call_handler_6 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2)
        .add_call(call_handler_3)
        .add_call(call_handler_4)
        .add_call(call_handler_5)
        .add_call(call_handler_6);

    let (val_1, val_2, type_1, type_2, array_1, array_2): (
        u64,
        u64,
        MyType,
        MyType,
        [u64; 2],
        [u64; 2],
    ) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 5);
    assert_eq!(val_2, 6);
    assert_eq!(type_1, my_type_1);
    assert_eq!(type_2, my_type_2);
    assert_eq!(array_1, [7; 2]);
    assert_eq!(array_2, [42; 2]);

    Ok(())
}

#[tokio::test]
async fn test_contract_call_fee_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let gas_limit = 800;
    let tolerance = Some(0.2);
    let block_horizon = Some(1);
    let expected_gas_used = 960;
    let expected_metered_bytes_size = 824;

    let estimated_transaction_cost = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit))
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?;

    assert_eq!(estimated_transaction_cost.gas_used, expected_gas_used);
    assert_eq!(
        estimated_transaction_cost.metered_bytes_size,
        expected_metered_bytes_size
    );

    Ok(())
}

#[tokio::test]
async fn contract_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let estimated_gas_used = contract_methods
        .initialize_counter(42)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn mult_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.initialize_counter(42);
    let call_handler_2 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let tolerance = Some(0.0);
    let block_horizon = Some(1);
    let estimated_gas_used = multi_call_handler
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = multi_call_handler.call::<(u64, [u64; 2])>().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn contract_method_call_respects_maturity() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BlockHeightContract",
            project = "e2e/sway/contracts/transaction_block_height"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BlockHeightContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_w_maturity = |maturity| {
        contract_instance
            .methods()
            .calling_this_will_produce_a_block()
            .with_tx_policies(TxPolicies::default().with_maturity(maturity))
    };

    call_w_maturity(1).call().await.expect(
        "should have passed since we're calling with a maturity \
         that is less or equal to the current block height",
    );

    call_w_maturity(3).call().await.expect_err(
        "should have failed since we're calling with a maturity \
         that is greater than the current block height",
    );

    Ok(())
}

#[tokio::test]
async fn test_auth_msg_sender_from_sdk() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "AuthContract",
            project = "e2e/sway/contracts/auth_testing_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "AuthContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Contract returns true if `msg_sender()` matches `wallet.address()`.
    let response = contract_instance
        .methods()
        .check_msg_sender(wallet.address())
        .call()
        .await?;

    assert!(response.value);
    Ok(())
}

#[tokio::test]
async fn test_large_return_data() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/large_return_data"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let res = contract_methods.get_id().call().await?;

    assert_eq!(
        res.value.0,
        [
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ]
    );

    // One word-sized string
    let res = contract_methods.get_small_string().call().await?;
    assert_eq!(res.value, "gggggggg");

    // Two word-sized string
    let res = contract_methods.get_large_string().call().await?;
    assert_eq!(res.value, "ggggggggg");

    // Large struct will be bigger than a `WORD`.
    let res = contract_methods.get_large_struct().call().await?;
    assert_eq!(res.value.foo, 12);
    assert_eq!(res.value.bar, 42);

    // Array will be returned in `ReturnData`.
    let res = contract_methods.get_large_array().call().await?;
    assert_eq!(res.value, [1, 2]);

    let res = contract_methods.get_contract_id().call().await?;

    // First `value` is from `CallResponse`.
    // Second `value` is from the `ContractId` type.
    assert_eq!(
        res.value,
        ContractId::from([
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ])
    );
    Ok(())
}

#[tokio::test]
async fn can_handle_function_called_new() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().new().call().await?.value;

    assert_eq!(response, 12345);
    Ok(())
}

#[tokio::test]
async fn test_contract_setup_macro_deploy_with_salt() -> Result<()> {
    // ANCHOR: contract_setup_macro_multi
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_caller_id = contract_caller_instance.contract_id();

    let contract_caller_id2 = contract_caller_instance2.contract_id();

    // Because we deploy with salt, we can deploy the same contract multiple times
    assert_ne!(contract_caller_id, contract_caller_id2);

    // The first contract can be called because they were deployed on the same provider
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance2
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);
    // ANCHOR_END: contract_setup_macro_multi

    Ok(())
}

#[tokio::test]
async fn test_wallet_getter() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(contract_instance.account().address(), wallet.address());
    //`contract_id()` is tested in
    // async fn test_contract_calling_contract() -> Result<()> {
    Ok(())
}

#[tokio::test]
async fn test_connect_wallet() -> Result<()> {
    // ANCHOR: contract_setup_macro_manual_wallet
    let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));

    let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
    let wallet = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    // ANCHOR_END: contract_setup_macro_manual_wallet

    // pay for call with wallet
    let tx_policies = TxPolicies::default()
        .with_tip(100)
        .with_script_gas_limit(1_000_000);

    contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm that funds have been deducted
    let wallet_balance = wallet.get_asset_balance(&Default::default()).await?;
    assert!(DEFAULT_COIN_AMOUNT > wallet_balance);

    // pay for call with wallet_2
    contract_instance
        .with_account(wallet_2.clone())
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm there are no changes to wallet, wallet_2 has been charged
    let wallet_balance_second_call = wallet.get_asset_balance(&Default::default()).await?;
    let wallet_2_balance = wallet_2.get_asset_balance(&Default::default()).await?;
    assert_eq!(wallet_balance_second_call, wallet_balance);
    assert!(DEFAULT_COIN_AMOUNT > wallet_2_balance);

    Ok(())
}

async fn setup_output_variable_estimation_test() -> Result<(
    Vec<WalletUnlocked>,
    [Identity; 3],
    AssetId,
    Bech32ContractId,
)> {
    let wallet_config = WalletsConfig::new(Some(3), None, None);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;

    let contract_id = Contract::load_from(
        "sway/contracts/token_ops/out/release/token_ops.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallets[0], TxPolicies::default())
    .await?;

    let mint_asset_id = contract_id.asset_id(&Bits256::zeroed());
    let addresses = wallets
        .iter()
        .map(|wallet| wallet.address().into())
        .collect::<Vec<_>>()
        .try_into()
        .unwrap();

    Ok((wallets, addresses, mint_asset_id, contract_id))
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());
    let contract_methods = contract_instance.methods();
    let amount = 1000;

    {
        // Should fail due to lack of output variables
        let response = contract_methods
            .mint_to_addresses(amount, addresses)
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    {
        // Should add 3 output variables automatically
        let _ = contract_methods
            .mint_to_addresses(amount, addresses)
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .call()
            .await?;

        for wallet in wallets.iter() {
            let balance = wallet.get_asset_balance(&mint_asset_id).await?;
            assert_eq!(balance, amount);
        }
    }

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallets[0].clone());
    let contract_methods = contract_instance.methods();
    const NUM_OF_CALLS: u64 = 3;
    let amount = 1000;
    let total_amount = amount * NUM_OF_CALLS;

    let mut multi_call_handler = CallHandler::new_multi_call(wallets[0].clone());
    for _ in 0..NUM_OF_CALLS {
        let call_handler = contract_methods.mint_to_addresses(amount, addresses);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    wallets[0]
        .force_transfer_to_contract(
            &contract_id,
            total_amount,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await
        .unwrap();

    let base_layer_address = Bits256([1u8; 32]);
    let call_handler = contract_methods.send_message(base_layer_address, amount);
    multi_call_handler = multi_call_handler.add_call(call_handler);

    let _ = multi_call_handler
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call::<((), (), ())>()
        .await?;

    for wallet in wallets.iter() {
        let balance = wallet.get_asset_balance(&mint_asset_id).await?;
        assert_eq!(balance, 3 * amount);
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_instance_get_balances() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let (coins, asset_ids) = setup_multiple_assets_coins(wallet.address(), 2, 4, 8);

    let random_asset_id = &asset_ids[1];
    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_id = contract_instance.contract_id();

    // Check the current balance of the contract with id 'contract_id'
    let contract_balances = contract_instance.get_balances().await?;
    assert!(contract_balances.is_empty());

    // Transfer an amount to the contract
    let amount = 8;
    wallet
        .force_transfer_to_contract(contract_id, amount, *random_asset_id, TxPolicies::default())
        .await?;

    // Check that the contract now has 1 coin
    let contract_balances = contract_instance.get_balances().await?;
    assert_eq!(contract_balances.len(), 1);

    let random_asset_balance = contract_balances.get(random_asset_id).unwrap();
    assert_eq!(*random_asset_balance, amount);

    Ok(())
}

#[tokio::test]
async fn contract_call_futures_implement_send() -> Result<()> {
    use std::future::Future;

    fn tokio_spawn_imitation<T>(_: T)
    where
        T: Future + Send + 'static,
    {
    }

    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    tokio_spawn_imitation(async move {
        contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await
            .unwrap();
    });
    Ok(())
}

#[tokio::test]
async fn test_contract_set_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let res = lib_contract_instance.methods().increment(42).call().await?;
    assert_eq!(43, res.value);

    {
        // Should fail due to missing external contracts
        let res = contract_caller_instance
            .methods()
            .increment_from_contract(lib_contract_id, 42)
            .call()
            .await;

        assert!(matches!(
            res,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    let res = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    assert_eq!(43, res.value);
    Ok(())
}

#[tokio::test]
async fn test_output_variable_contract_id_estimation_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_test_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_methods = contract_caller_instance.methods();

    let mut multi_call_handler =
        CallHandler::new_multi_call(wallet.clone()).with_tx_policies(Default::default());

    for _ in 0..3 {
        let call_handler = contract_methods.increment_from_contract(lib_contract_id, 42);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    // add call that does not need ContractId
    let contract_methods = contract_test_instance.methods();
    let call_handler = contract_methods.get(5, 6);

    multi_call_handler = multi_call_handler.add_call(call_handler);

    let call_response = multi_call_handler
        .determine_missing_contracts(None)
        .await?
        .call::<(u64, u64, u64, u64)>()
        .await?;

    assert_eq!(call_response.value, (43, 43, 43, 11));

    Ok(())
}

#[tokio::test]
async fn test_contract_call_with_non_default_max_input() -> Result<()> {
    use fuels::{
        tx::{ConsensusParameters, TxParameters},
        types::coin::Coin,
    };

    let mut consensus_parameters = ConsensusParameters::default();
    let tx_params = TxParameters::default()
        .with_max_inputs(123)
        .with_max_size(1_000_000);
    consensus_parameters.set_tx_params(tx_params);
    let contract_params = ContractParameters::default().with_contract_max_size(1_000_000);
    consensus_parameters.set_contract_params(contract_params);

    let mut wallet = WalletUnlocked::new_random(None);

    let coins: Vec<Coin> = setup_single_asset_coins(
        wallet.address(),
        Default::default(),
        DEFAULT_NUM_COINS,
        DEFAULT_COIN_AMOUNT,
    );
    let chain_config = ChainConfig {
        consensus_parameters: consensus_parameters.clone(),
        ..ChainConfig::default()
    };

    let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
    wallet.set_provider(provider.clone());
    assert_eq!(consensus_parameters, provider.consensus_parameters().await?);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn test_add_custom_assets() -> Result<()> {
    let initial_amount = 100_000;
    let asset_base = AssetConfig {
        id: AssetId::zeroed(),
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_1 = AssetId::from([3u8; 32]);
    let asset_1 = AssetConfig {
        id: asset_id_1,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_2 = AssetId::from([1u8; 32]);
    let asset_2 = AssetConfig {
        id: asset_id_2,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let assets = vec![asset_base, asset_1, asset_2];

    let num_wallets = 2;
    let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
    let mut wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
    let wallet_1 = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet_1",
            random_salt = false,
        ),
    );

    let amount_1 = 5000;
    let amount_2 = 3000;
    let response = contract_instance
        .methods()
        .get(5, 6)
        .add_custom_asset(asset_id_1, amount_1, Some(wallet_2.address().clone()))
        .add_custom_asset(asset_id_2, amount_2, Some(wallet_2.address().clone()))
        .call()
        .await?;

    assert_eq!(response.value, 11);

    let balance_asset_1 = wallet_1.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_1.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount - amount_1);
    assert_eq!(balance_asset_2, initial_amount - amount_2);

    let balance_asset_1 = wallet_2.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_2.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount + amount_1);
    assert_eq!(balance_asset_2, initial_amount + amount_2);

    Ok(())
}

#[tokio::test]
async fn contract_load_error_messages() {
    {
        let binary_path = "sway/contracts/contract_test/out/release/no_file_on_path.bin";
        let expected_error = format!("io: file \"{binary_path}\" does not exist");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
    {
        let binary_path = "sway/contracts/contract_test/out/release/contract_test-abi.json";
        let expected_error = format!("expected \"{binary_path}\" to have '.bin' extension");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
}

#[tokio::test]
async fn test_payable_annotation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/payable_annotation"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .payable()
        .call_params(
            CallParameters::default()
                .with_amount(100)
                .with_gas_forwarded(20_000),
        )?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    // ANCHOR: non_payable_params
    let err = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_amount(100))
        .expect_err("should return error");

    assert!(matches!(err, Error::Other(s) if s.contains("assets forwarded to non-payable method")));
    // ANCHOR_END: non_payable_params

    let response = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_gas_forwarded(20_000))?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    Ok(())
}

#[tokio::test]
async fn multi_call_from_calls_with_different_account_types() -> Result<()> {
    use fuels::prelude::*;

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallet = WalletUnlocked::new_random(None);
    let predicate = Predicate::from_code(vec![]);

    let contract_methods_wallet =
        MyContract::new(Bech32ContractId::default(), wallet.clone()).methods();
    let contract_methods_predicate =
        MyContract::new(Bech32ContractId::default(), predicate).methods();

    let call_handler_1 = contract_methods_wallet.initialize_counter(42);
    let call_handler_2 = contract_methods_predicate.get_array([42; 2]);

    let _multi_call_handler = CallHandler::new_multi_call(wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    Ok(())
}

#[tokio::test]
async fn low_level_call() -> Result<()> {
    use fuels::types::SizedAsciiString;

    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyCallerContract",
                project = "e2e/sway/contracts/low_level_caller"
            ),
            Contract(
                name = "MyTargetContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "caller_contract_instance",
            contract = "MyCallerContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "target_contract_instance",
            contract = "MyTargetContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let function_selector = encode_fn_selector("initialize_counter");
    let call_data = calldata!(42u64)?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let response = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await?;
    assert_eq!(response.value, 42);

    let function_selector = encode_fn_selector("set_value_multiple_complex");
    let call_data = calldata!(
        MyStruct {
            a: true,
            b: [1, 2, 3],
        },
        SizedAsciiString::<4>::try_from("fuel")?
    )?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let result_uint = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await
        .unwrap()
        .value;

    let result_bool = target_contract_instance
        .methods()
        .get_bool_value()
        .call()
        .await
        .unwrap()
        .value;

    let result_str = target_contract_instance
        .methods()
        .get_str_value()
        .call()
        .await
        .unwrap()
        .value;

    assert_eq!(result_uint, 42);
    assert!(result_bool);
    assert_eq!(result_str, "fuel");

    Ok(())
}

#[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
#[test]
fn db_rocksdb() {
    use std::{fs, str::FromStr};

    use fuels::{
        accounts::wallet::WalletUnlocked,
        client::{PageDirection, PaginationRequest},
        crypto::SecretKey,
        prelude::{setup_test_provider, DbType, Error, ViewOnlyAccount, DEFAULT_COIN_AMOUNT},
    };

    let temp_dir = tempfile::tempdir().expect("failed to make tempdir");
    let temp_dir_name = temp_dir
        .path()
        .file_name()
        .expect("failed to get file name")
        .to_string_lossy()
        .to_string();
    let temp_database_path = temp_dir.path().join("db");

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let _ = temp_dir;
            let wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            const NUMBER_OF_ASSETS: u64 = 2;
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let chain_config = ChainConfig {
                chain_name: temp_dir_name.clone(),
                consensus_parameters: Default::default(),
                ..ChainConfig::local_testnet()
            };

            let (coins, _) = setup_multiple_assets_coins(
                wallet.address(),
                NUMBER_OF_ASSETS,
                DEFAULT_NUM_COINS,
                DEFAULT_COIN_AMOUNT,
            );

            let provider =
                setup_test_provider(coins.clone(), vec![], Some(node_config), Some(chain_config))
                    .await?;

            provider.produce_blocks(2, None).await?;

            Ok::<(), Error>(())
        })
        .unwrap();

    // The runtime needs to be terminated because the node can currently only be killed when the runtime itself shuts down.

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let provider = setup_test_provider(vec![], vec![], Some(node_config), None).await?;
            // the same wallet that was used when rocksdb was built. When we connect it to the provider, we expect it to have the same amount of assets
            let mut wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            wallet.set_provider(provider.clone());

            let blocks = provider
                .get_blocks(PaginationRequest {
                    cursor: None,
                    results: 10,
                    direction: PageDirection::Forward,
                })
                .await?
                .results;

            assert_eq!(blocks.len(), 3);
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(wallet.get_balances().await?.len(), 2);

            fs::remove_dir_all(
                temp_database_path
                    .parent()
                    .expect("db parent folder does not exist"),
            )?;

            Ok::<(), Error>(())
        })
        .unwrap();
}

#[tokio::test]
async fn can_configure_decoding_of_contract_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: Will not work if max_tokens not big enough
        methods.i_return_a_1k_el_array().with_decoder_config(DecoderConfig{max_tokens: 100, ..Default::default()}).call().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Single call: Works when limit is bumped
        let result = methods
            .i_return_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(result, [0; 1000]);
    }
    {
        // Multi call: Will not work if max_tokens not big enough
        CallHandler::new_multi_call(wallet.clone())
         .add_call(methods.i_return_a_1k_el_array())
         .with_decoder_config(DecoderConfig { max_tokens: 100, ..Default::default() })
         .call::<([u8; 1000],)>().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Multi call: Works when configured
        CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_return_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<([u8; 1000],)>()
            .await
            .unwrap();
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let submitted_tx = contract_methods.get(1, 2).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;

    assert_eq!(value, 3);

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let handle = multi_call_handler.submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let (val_1, val_2): (u64, u64) = handle.response().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_heap_type_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vector_output"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        let call_handler_1 = contract_instance.methods().u8_in_vec(5);
        let call_handler_2 = contract_instance_2.methods().get_single(7);
        let call_handler_3 = contract_instance.methods().u8_in_vec(3);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2)
            .add_call(call_handler_3);

        let (val_1, val_2, val_3): (Vec<u8>, u64, Vec<u8>) = multi_call_handler.call().await?.value;

        assert_eq!(val_1, vec![0, 1, 2, 3, 4]);
        assert_eq!(val_2, 7);
        assert_eq!(val_3, vec![0, 1, 2]);
    }

    Ok(())
}

#[tokio::test]
async fn heap_types_correctly_offset_in_create_transactions_w_storage_slots() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Predicate(
            name = "MyPredicate",
            project = "e2e/sway/types/predicates/predicate_vector"
        ),),
    );

    let provider = wallet.try_provider()?.clone();
    let data = MyPredicateEncoder::default().encode_data(18, 24, vec![2, 4, 42])?;
    let predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(data)
    .with_provider(provider);

    wallet
        .transfer(
            predicate.address(),
            10_000,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await?;

    // if the contract is successfully deployed then the predicate was unlocked. This further means
    // the offsets were setup correctly since the predicate uses heap types in its arguments.
    // Storage slots were loaded automatically by default
    Contract::load_from(
        "sway/contracts/storage/out/release/storage.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    Ok(())
}

#[tokio::test]
async fn test_arguments_with_gas_forwarded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vectors"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let x = 128;
    let vec_input = vec![0, 1, 2];
    {
        let response = contract_instance
            .methods()
            .get_single(x)
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;

        assert_eq!(response.value, x);
    }
    {
        contract_instance_2
            .methods()
            .u32_vec(vec_input.clone())
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;
    }
    {
        let call_handler_1 = contract_instance.methods().get_single(x);
        let call_handler_2 = contract_instance_2.methods().u32_vec(vec_input);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let (value, _): (u64, ()) = multi_call_handler.call().await?.value;

        assert_eq!(value, x);
    }

    Ok(())
}

#[tokio::test]
async fn contract_custom_call_no_signatures_strategy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let provider = wallet.try_provider()?;

    let counter = 42;
    let call_handler = contract_instance.methods().initialize_counter(counter);

    let mut tb = call_handler.transaction_builder().await?;

    let base_asset_id = *provider.consensus_parameters().await?.base_asset_id();

    let amount = 10;
    let consensus_parameters = provider.consensus_parameters().await?;
    let new_base_inputs = wallet
        .get_asset_inputs_for_amount(base_asset_id, amount, None)
        .await?;
    tb.inputs_mut().extend(new_base_inputs);
    tb.outputs_mut()
        .push(Output::change(wallet.address().into(), 0, base_asset_id));

    // ANCHOR: tb_no_signatures_strategy
    let mut tx = tb
        .with_build_strategy(ScriptBuildStrategy::NoSignatures)
        .build(provider)
        .await?;
    // ANCHOR: tx_sign_with
    tx.sign_with(&wallet, consensus_parameters.chain_id())
        .await?;
    // ANCHOR_END: tx_sign_with
    // ANCHOR_END: tb_no_signatures_strategy

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;

    let tx_status = provider.tx_status(&tx_id).await?;

    let response = call_handler.get_response_from(tx_status)?;

    assert_eq!(counter, response.value);

    Ok(())
}

#[tokio::test]
async fn contract_encoder_config_is_applied() -> Result<()> {
    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Wallets("wallet")
    );
    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let instance = TestContract::new(contract_id.clone(), wallet.clone());

    {
        let _encoding_ok = instance
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let instance_with_encoder_config = instance.with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));

        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));
    }

    Ok(())
}

#[tokio::test]
async fn test_reentrant_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LibContractCaller",
            project = "e2e/sway/contracts/lib_contract_caller"
        ),),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = contract_caller_instance.contract_id();
    let response = contract_caller_instance
        .methods()
        .re_entrant(contract_id, true)
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn msg_sender_gas_estimation_issue() {
    // Gas estimation requires an input of the base asset. If absent, a fake input is
    // added. However, if a non-base coin is present and the fake input introduces a
    // second owner, it causes the `msg_sender` sway fn to fail. This leads
    // to a premature failure in gas estimation, risking transaction failure due to
    // a low gas limit.
    let mut wallet = WalletUnlocked::new_random(None);

    let (coins, ids) =
        setup_multiple_assets_coins(wallet.address(), 2, DEFAULT_NUM_COINS, DEFAULT_COIN_AMOUNT);

    let provider = setup_test_provider(coins, vec![], None, None)
        .await
        .unwrap();
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/msg_methods"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let asset_id = ids[0];

    // The fake coin won't be added if we add a base asset, so let's not do that
    assert!(
        asset_id
            != *provider
                .consensus_parameters()
                .await
                .unwrap()
                .base_asset_id()
    );
    let call_params = CallParameters::default()
        .with_amount(100)
        .with_asset_id(asset_id);

    contract_instance
        .methods()
        .message_sender()
        .call_params(call_params)
        .unwrap()
        .call()
        .await
        .unwrap();
}

#[tokio::test]
async fn variable_output_estimation_is_optimized() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/var_outputs"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_methods = contract_instance.methods();

    let coins = 252;
    let recipient = Identity::Address(wallet.address().into());
    let start = Instant::now();
    let _ = contract_methods
        .mint(coins, recipient)
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    // debug builds are slower (20x for `fuel-core-lib`, 4x for a release-fuel-core-binary)
    // we won't validate in that case so we don't have to maintain two expectations
    if !cfg!(debug_assertions) {
        let elapsed = start.elapsed().as_secs();
        let limit = 2;
        if elapsed > limit {
            panic!("Estimation took too long ({elapsed}). Limit is {limit}");
        }
    }

    Ok(())
}

async fn setup_node_with_high_price() -> Result<Vec<WalletUnlocked>> {
    let wallet_config = WalletsConfig::new(None, None, None);
    let fee_parameters = FeeParameters::V1(FeeParametersV1 {
        gas_price_factor: 92000,
        gas_per_byte: 63,
    });
    let consensus_parameters = ConsensusParameters::V1(ConsensusParametersV1 {
        fee_params: fee_parameters,
        ..Default::default()
    });
    let node_config = Some(NodeConfig {
        starting_gas_price: 1100,
        ..NodeConfig::default()
    });
    let chain_config = ChainConfig {
        consensus_parameters,
        ..ChainConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(wallet_config, node_config, Some(chain_config))
            .await?;

    Ok(wallets)
}

#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);

    let response = MyContract::new(contract_id, no_funds_wallet.clone())
        .methods()
        .get(5, 6)
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn simulations_can_be_made_without_coins_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let contract_instance = MyContract::new(contract_id, no_funds_wallet.clone());

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.get(1, 2);
    let call_handler_2 = contract_methods.get(3, 4);

    let mut multi_call_handler = CallHandler::new_multi_call(no_funds_wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let value: (u64, u64) = multi_call_handler
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value, (3, 7));

    Ok(())
}

#[tokio::test]
async fn contract_call_with_non_zero_base_asset_id_and_tip() -> Result<()> {
    use fuels::{prelude::*, tx::ConsensusParameters};

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let asset_id = AssetId::new([1; 32]);

    let mut consensus_parameters = ConsensusParameters::default();
    consensus_parameters.set_base_asset_id(asset_id);

    let config = ChainConfig {
        consensus_parameters,
        ..Default::default()
    };

    let asset_base = AssetConfig {
        id: asset_id,
        num_coins: 1,
        coin_amount: 10_000,
    };

    let wallet_config = WalletsConfig::new_multiple_assets(1, vec![asset_base]);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, Some(config)).await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_tip(10))
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn max_fee_estimation_respects_tolerance() -> Result<()> {
    use fuels::prelude::*;

    let mut call_wallet = WalletUnlocked::new_random(None);

    let call_coins = setup_single_asset_coins(call_wallet.address(), AssetId::BASE, 1000, 1);

    let mut deploy_wallet = WalletUnlocked::new_random(None);
    let deploy_coins =
        setup_single_asset_coins(deploy_wallet.address(), AssetId::BASE, 1, 1_000_000);

    let provider =
        setup_test_provider([call_coins, deploy_coins].concat(), vec![], None, None).await?;

    call_wallet.set_provider(provider.clone());
    deploy_wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            wallet = "deploy_wallet",
            contract = "MyContract",
            random_salt = false,
        )
    );
    let contract_instance = contract_instance.with_account(call_wallet.clone());

    let max_fee_from_tx = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            let builder = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap();

            assert_eq!(
                builder.max_fee_estimation_tolerance, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
                "Expected pre-set tolerance"
            );

            builder
                .with_max_fee_estimation_tolerance(tolerance)
                .build(&provider)
                .await
                .unwrap()
                .max_fee()
                .unwrap()
        }
    };

    let max_fee_from_builder = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance)
                .estimate_max_fee(&provider)
                .await
                .unwrap()
        }
    };

    let base_amount_in_inputs = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let call_wallet = &call_wallet;
        async move {
            let mut tb = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance);

            call_wallet.adjust_for_fee(&mut tb, 0).await.unwrap();
            tb.inputs
                .iter()
                .filter_map(|input: &Input| match input {
                    Input::ResourceSigned { resource }
                        if resource.coin_asset_id().unwrap() == AssetId::BASE =>
                    {
                        Some(resource.amount())
                    }
                    _ => None,
                })
                .sum::<u64>()
        }
    };

    let no_increase_max_fee = max_fee_from_tx(0.0).await;
    let increased_max_fee = max_fee_from_tx(2.00).await;

    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let no_increase_max_fee = max_fee_from_builder(0.0).await;
    let increased_max_fee = max_fee_from_builder(2.00).await;
    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let normal_base_asset = base_amount_in_inputs(0.0).await;
    let more_base_asset_due_to_bigger_tolerance = base_amount_in_inputs(5.00).await;
    assert!(more_base_asset_due_to_bigger_tolerance > normal_base_asset);

    Ok(())
}

#[tokio::test]
async fn blob_contract_deployment() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
    ));

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract_size = std::fs::metadata(contract_binary)
        .expect("contract file not found")
        .len();

    assert!(
         contract_size > 150_000,
         "the testnet size limit was around 100kB, we want a contract bigger than that to reflect prod (current: {contract_size}B)"
     );

    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::new(Some(2), None, None), None, None)
            .await?;

    let provider = wallets[0].provider().unwrap().clone();

    let consensus_parameters = provider.consensus_parameters().await?;

    let contract_max_size = consensus_parameters.contract_params().contract_max_size();
    assert!(
         contract_size > contract_max_size,
         "this test should ideally be run with a contract bigger than the max contract size ({contract_max_size}B) so that we know deployment couldn't have happened without blobs"
     );

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallets[0], TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn regular_contract_can_be_deployed() -> Result<()> {
    // given
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
    );

    let contract_binary = "sway/contracts/contract_test/out/release/contract_test.bin";

    // when
    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    // then
    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance
        .methods()
        .get_counter()
        .call()
        .await?
        .value;

    assert_eq!(response, 0);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_be_deployed_directly() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_upload_blobs_separately_then_deploy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    let blob_ids = contract.blob_ids();

    // if this were an example for the user we'd just call `deploy` on the contract above
    // this way we are testing that the blobs were really deployed above, otherwise the following
    // would fail
    let contract_id = Contract::loader_from_blob_ids(
        blob_ids.to_vec(),
        contract.salt(),
        contract.storage_slots().to_vec(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_blob_already_uploaded_not_an_issue() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?;

    // this will upload blobs
    contract
        .clone()
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    // this will try to upload the blobs but skip upon encountering an error
    let contract_id = contract
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .something()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_storage_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_storage_slots = contract.storage_slots().to_vec();

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";
    let proxy_contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let combined_storage_slots = [&contract_storage_slots, proxy_contract.storage_slots()].concat();

    let proxy_id = proxy_contract
        .with_storage_slots(combined_storage_slots)
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?
        .value;

    assert_eq!(response, 42);

    let _res = proxy
        .methods()
        .write_some_u64(36)
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 36);

    Ok(())
}\n```

If however, you do not need to decode logs or you do not have a contract instance that was generated using the abigen macro you can use with_contract_ids(&[&contract_id, ...]) and provide the required contract ids.

```rust\nuse std::time::Duration;

use fuel_tx::{
    consensus_parameters::{ConsensusParametersV1, FeeParametersV1},
    ConsensusParameters, FeeParameters, Output,
};
use fuels::{
    core::codec::{calldata, encode_fn_selector, DecoderConfig, EncoderConfig},
    prelude::*,
    programs::DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
    tx::ContractParameters,
    types::{errors::transaction::Reason, input::Input, Bits256, Identity},
};
use tokio::time::Instant;

#[tokio::test]
async fn test_multiple_args() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Make sure we can call the contract with multiple arguments
    let contract_methods = contract_instance.methods();
    let response = contract_methods.get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    let t = MyType { x: 5, y: 6 };
    let response = contract_methods.get_alt(t.clone()).call().await?;
    assert_eq!(response.value, t);

    let response = contract_methods.get_single(5).call().await?;
    assert_eq!(response.value, 5);
    Ok(())
}

#[tokio::test]
async fn test_contract_calling_contract() -> Result<()> {
    // Tests a contract call that calls another contract (FooCaller calls FooContract underneath)
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "lib_contract_instance2",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();
    let lib_contract_id2 = lib_contract_instance2.contract_id();

    // Call the contract directly. It increments the given value.
    let response = lib_contract_instance.methods().increment(42).call().await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance
        .methods()
        .increment_from_contracts(lib_contract_id, lib_contract_id2, 42)
        // Note that the two lib_contract_instances have different types
        .with_contracts(&[&lib_contract_instance, &lib_contract_instance2])
        .call()
        .await?;

    assert_eq!(86, response.value);

    // ANCHOR: external_contract
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    assert_eq!(43, response.value);

    // ANCHOR: external_contract_ids
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contract_ids(&[lib_contract_id.clone()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    assert_eq!(43, response.value);
    Ok(())
}

#[tokio::test]
async fn test_reverting_transaction() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertContract",
            project = "e2e/sway/contracts/revert_transaction_error"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .make_transaction_fail(true)
        .call()
        .await;

    assert!(matches!(
        response,
        Err(Error::Transaction(Reason::Reverted { revert_id, .. })) if revert_id == 128
    ));

    Ok(())
}

#[tokio::test]
async fn test_multiple_read_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MultiReadContract",
            project = "e2e/sway/contracts/multiple_read_calls"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MultiReadContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    contract_methods.store(42).call().await?;

    // Use "simulate" because the methods don't actually
    // run a transaction, but just a dry-run
    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);

    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_multi_call_beginner() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let (val_1, val_2): (u64, u64) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_pro() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let my_type_1 = MyType { x: 1, y: 2 };
    let my_type_2 = MyType { x: 3, y: 4 };

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(5);
    let call_handler_2 = contract_methods.get_single(6);
    let call_handler_3 = contract_methods.get_alt(my_type_1.clone());
    let call_handler_4 = contract_methods.get_alt(my_type_2.clone());
    let call_handler_5 = contract_methods.get_array([7; 2]);
    let call_handler_6 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2)
        .add_call(call_handler_3)
        .add_call(call_handler_4)
        .add_call(call_handler_5)
        .add_call(call_handler_6);

    let (val_1, val_2, type_1, type_2, array_1, array_2): (
        u64,
        u64,
        MyType,
        MyType,
        [u64; 2],
        [u64; 2],
    ) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 5);
    assert_eq!(val_2, 6);
    assert_eq!(type_1, my_type_1);
    assert_eq!(type_2, my_type_2);
    assert_eq!(array_1, [7; 2]);
    assert_eq!(array_2, [42; 2]);

    Ok(())
}

#[tokio::test]
async fn test_contract_call_fee_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let gas_limit = 800;
    let tolerance = Some(0.2);
    let block_horizon = Some(1);
    let expected_gas_used = 960;
    let expected_metered_bytes_size = 824;

    let estimated_transaction_cost = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit))
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?;

    assert_eq!(estimated_transaction_cost.gas_used, expected_gas_used);
    assert_eq!(
        estimated_transaction_cost.metered_bytes_size,
        expected_metered_bytes_size
    );

    Ok(())
}

#[tokio::test]
async fn contract_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let estimated_gas_used = contract_methods
        .initialize_counter(42)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn mult_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.initialize_counter(42);
    let call_handler_2 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let tolerance = Some(0.0);
    let block_horizon = Some(1);
    let estimated_gas_used = multi_call_handler
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = multi_call_handler.call::<(u64, [u64; 2])>().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn contract_method_call_respects_maturity() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BlockHeightContract",
            project = "e2e/sway/contracts/transaction_block_height"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BlockHeightContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_w_maturity = |maturity| {
        contract_instance
            .methods()
            .calling_this_will_produce_a_block()
            .with_tx_policies(TxPolicies::default().with_maturity(maturity))
    };

    call_w_maturity(1).call().await.expect(
        "should have passed since we're calling with a maturity \
         that is less or equal to the current block height",
    );

    call_w_maturity(3).call().await.expect_err(
        "should have failed since we're calling with a maturity \
         that is greater than the current block height",
    );

    Ok(())
}

#[tokio::test]
async fn test_auth_msg_sender_from_sdk() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "AuthContract",
            project = "e2e/sway/contracts/auth_testing_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "AuthContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Contract returns true if `msg_sender()` matches `wallet.address()`.
    let response = contract_instance
        .methods()
        .check_msg_sender(wallet.address())
        .call()
        .await?;

    assert!(response.value);
    Ok(())
}

#[tokio::test]
async fn test_large_return_data() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/large_return_data"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let res = contract_methods.get_id().call().await?;

    assert_eq!(
        res.value.0,
        [
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ]
    );

    // One word-sized string
    let res = contract_methods.get_small_string().call().await?;
    assert_eq!(res.value, "gggggggg");

    // Two word-sized string
    let res = contract_methods.get_large_string().call().await?;
    assert_eq!(res.value, "ggggggggg");

    // Large struct will be bigger than a `WORD`.
    let res = contract_methods.get_large_struct().call().await?;
    assert_eq!(res.value.foo, 12);
    assert_eq!(res.value.bar, 42);

    // Array will be returned in `ReturnData`.
    let res = contract_methods.get_large_array().call().await?;
    assert_eq!(res.value, [1, 2]);

    let res = contract_methods.get_contract_id().call().await?;

    // First `value` is from `CallResponse`.
    // Second `value` is from the `ContractId` type.
    assert_eq!(
        res.value,
        ContractId::from([
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ])
    );
    Ok(())
}

#[tokio::test]
async fn can_handle_function_called_new() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().new().call().await?.value;

    assert_eq!(response, 12345);
    Ok(())
}

#[tokio::test]
async fn test_contract_setup_macro_deploy_with_salt() -> Result<()> {
    // ANCHOR: contract_setup_macro_multi
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_caller_id = contract_caller_instance.contract_id();

    let contract_caller_id2 = contract_caller_instance2.contract_id();

    // Because we deploy with salt, we can deploy the same contract multiple times
    assert_ne!(contract_caller_id, contract_caller_id2);

    // The first contract can be called because they were deployed on the same provider
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance2
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);
    // ANCHOR_END: contract_setup_macro_multi

    Ok(())
}

#[tokio::test]
async fn test_wallet_getter() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(contract_instance.account().address(), wallet.address());
    //`contract_id()` is tested in
    // async fn test_contract_calling_contract() -> Result<()> {
    Ok(())
}

#[tokio::test]
async fn test_connect_wallet() -> Result<()> {
    // ANCHOR: contract_setup_macro_manual_wallet
    let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));

    let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
    let wallet = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    // ANCHOR_END: contract_setup_macro_manual_wallet

    // pay for call with wallet
    let tx_policies = TxPolicies::default()
        .with_tip(100)
        .with_script_gas_limit(1_000_000);

    contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm that funds have been deducted
    let wallet_balance = wallet.get_asset_balance(&Default::default()).await?;
    assert!(DEFAULT_COIN_AMOUNT > wallet_balance);

    // pay for call with wallet_2
    contract_instance
        .with_account(wallet_2.clone())
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm there are no changes to wallet, wallet_2 has been charged
    let wallet_balance_second_call = wallet.get_asset_balance(&Default::default()).await?;
    let wallet_2_balance = wallet_2.get_asset_balance(&Default::default()).await?;
    assert_eq!(wallet_balance_second_call, wallet_balance);
    assert!(DEFAULT_COIN_AMOUNT > wallet_2_balance);

    Ok(())
}

async fn setup_output_variable_estimation_test() -> Result<(
    Vec<WalletUnlocked>,
    [Identity; 3],
    AssetId,
    Bech32ContractId,
)> {
    let wallet_config = WalletsConfig::new(Some(3), None, None);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;

    let contract_id = Contract::load_from(
        "sway/contracts/token_ops/out/release/token_ops.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallets[0], TxPolicies::default())
    .await?;

    let mint_asset_id = contract_id.asset_id(&Bits256::zeroed());
    let addresses = wallets
        .iter()
        .map(|wallet| wallet.address().into())
        .collect::<Vec<_>>()
        .try_into()
        .unwrap();

    Ok((wallets, addresses, mint_asset_id, contract_id))
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());
    let contract_methods = contract_instance.methods();
    let amount = 1000;

    {
        // Should fail due to lack of output variables
        let response = contract_methods
            .mint_to_addresses(amount, addresses)
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    {
        // Should add 3 output variables automatically
        let _ = contract_methods
            .mint_to_addresses(amount, addresses)
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .call()
            .await?;

        for wallet in wallets.iter() {
            let balance = wallet.get_asset_balance(&mint_asset_id).await?;
            assert_eq!(balance, amount);
        }
    }

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallets[0].clone());
    let contract_methods = contract_instance.methods();
    const NUM_OF_CALLS: u64 = 3;
    let amount = 1000;
    let total_amount = amount * NUM_OF_CALLS;

    let mut multi_call_handler = CallHandler::new_multi_call(wallets[0].clone());
    for _ in 0..NUM_OF_CALLS {
        let call_handler = contract_methods.mint_to_addresses(amount, addresses);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    wallets[0]
        .force_transfer_to_contract(
            &contract_id,
            total_amount,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await
        .unwrap();

    let base_layer_address = Bits256([1u8; 32]);
    let call_handler = contract_methods.send_message(base_layer_address, amount);
    multi_call_handler = multi_call_handler.add_call(call_handler);

    let _ = multi_call_handler
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call::<((), (), ())>()
        .await?;

    for wallet in wallets.iter() {
        let balance = wallet.get_asset_balance(&mint_asset_id).await?;
        assert_eq!(balance, 3 * amount);
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_instance_get_balances() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let (coins, asset_ids) = setup_multiple_assets_coins(wallet.address(), 2, 4, 8);

    let random_asset_id = &asset_ids[1];
    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_id = contract_instance.contract_id();

    // Check the current balance of the contract with id 'contract_id'
    let contract_balances = contract_instance.get_balances().await?;
    assert!(contract_balances.is_empty());

    // Transfer an amount to the contract
    let amount = 8;
    wallet
        .force_transfer_to_contract(contract_id, amount, *random_asset_id, TxPolicies::default())
        .await?;

    // Check that the contract now has 1 coin
    let contract_balances = contract_instance.get_balances().await?;
    assert_eq!(contract_balances.len(), 1);

    let random_asset_balance = contract_balances.get(random_asset_id).unwrap();
    assert_eq!(*random_asset_balance, amount);

    Ok(())
}

#[tokio::test]
async fn contract_call_futures_implement_send() -> Result<()> {
    use std::future::Future;

    fn tokio_spawn_imitation<T>(_: T)
    where
        T: Future + Send + 'static,
    {
    }

    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    tokio_spawn_imitation(async move {
        contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await
            .unwrap();
    });
    Ok(())
}

#[tokio::test]
async fn test_contract_set_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let res = lib_contract_instance.methods().increment(42).call().await?;
    assert_eq!(43, res.value);

    {
        // Should fail due to missing external contracts
        let res = contract_caller_instance
            .methods()
            .increment_from_contract(lib_contract_id, 42)
            .call()
            .await;

        assert!(matches!(
            res,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    let res = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    assert_eq!(43, res.value);
    Ok(())
}

#[tokio::test]
async fn test_output_variable_contract_id_estimation_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_test_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_methods = contract_caller_instance.methods();

    let mut multi_call_handler =
        CallHandler::new_multi_call(wallet.clone()).with_tx_policies(Default::default());

    for _ in 0..3 {
        let call_handler = contract_methods.increment_from_contract(lib_contract_id, 42);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    // add call that does not need ContractId
    let contract_methods = contract_test_instance.methods();
    let call_handler = contract_methods.get(5, 6);

    multi_call_handler = multi_call_handler.add_call(call_handler);

    let call_response = multi_call_handler
        .determine_missing_contracts(None)
        .await?
        .call::<(u64, u64, u64, u64)>()
        .await?;

    assert_eq!(call_response.value, (43, 43, 43, 11));

    Ok(())
}

#[tokio::test]
async fn test_contract_call_with_non_default_max_input() -> Result<()> {
    use fuels::{
        tx::{ConsensusParameters, TxParameters},
        types::coin::Coin,
    };

    let mut consensus_parameters = ConsensusParameters::default();
    let tx_params = TxParameters::default()
        .with_max_inputs(123)
        .with_max_size(1_000_000);
    consensus_parameters.set_tx_params(tx_params);
    let contract_params = ContractParameters::default().with_contract_max_size(1_000_000);
    consensus_parameters.set_contract_params(contract_params);

    let mut wallet = WalletUnlocked::new_random(None);

    let coins: Vec<Coin> = setup_single_asset_coins(
        wallet.address(),
        Default::default(),
        DEFAULT_NUM_COINS,
        DEFAULT_COIN_AMOUNT,
    );
    let chain_config = ChainConfig {
        consensus_parameters: consensus_parameters.clone(),
        ..ChainConfig::default()
    };

    let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
    wallet.set_provider(provider.clone());
    assert_eq!(consensus_parameters, provider.consensus_parameters().await?);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn test_add_custom_assets() -> Result<()> {
    let initial_amount = 100_000;
    let asset_base = AssetConfig {
        id: AssetId::zeroed(),
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_1 = AssetId::from([3u8; 32]);
    let asset_1 = AssetConfig {
        id: asset_id_1,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_2 = AssetId::from([1u8; 32]);
    let asset_2 = AssetConfig {
        id: asset_id_2,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let assets = vec![asset_base, asset_1, asset_2];

    let num_wallets = 2;
    let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
    let mut wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
    let wallet_1 = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet_1",
            random_salt = false,
        ),
    );

    let amount_1 = 5000;
    let amount_2 = 3000;
    let response = contract_instance
        .methods()
        .get(5, 6)
        .add_custom_asset(asset_id_1, amount_1, Some(wallet_2.address().clone()))
        .add_custom_asset(asset_id_2, amount_2, Some(wallet_2.address().clone()))
        .call()
        .await?;

    assert_eq!(response.value, 11);

    let balance_asset_1 = wallet_1.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_1.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount - amount_1);
    assert_eq!(balance_asset_2, initial_amount - amount_2);

    let balance_asset_1 = wallet_2.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_2.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount + amount_1);
    assert_eq!(balance_asset_2, initial_amount + amount_2);

    Ok(())
}

#[tokio::test]
async fn contract_load_error_messages() {
    {
        let binary_path = "sway/contracts/contract_test/out/release/no_file_on_path.bin";
        let expected_error = format!("io: file \"{binary_path}\" does not exist");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
    {
        let binary_path = "sway/contracts/contract_test/out/release/contract_test-abi.json";
        let expected_error = format!("expected \"{binary_path}\" to have '.bin' extension");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
}

#[tokio::test]
async fn test_payable_annotation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/payable_annotation"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .payable()
        .call_params(
            CallParameters::default()
                .with_amount(100)
                .with_gas_forwarded(20_000),
        )?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    // ANCHOR: non_payable_params
    let err = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_amount(100))
        .expect_err("should return error");

    assert!(matches!(err, Error::Other(s) if s.contains("assets forwarded to non-payable method")));
    // ANCHOR_END: non_payable_params

    let response = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_gas_forwarded(20_000))?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    Ok(())
}

#[tokio::test]
async fn multi_call_from_calls_with_different_account_types() -> Result<()> {
    use fuels::prelude::*;

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallet = WalletUnlocked::new_random(None);
    let predicate = Predicate::from_code(vec![]);

    let contract_methods_wallet =
        MyContract::new(Bech32ContractId::default(), wallet.clone()).methods();
    let contract_methods_predicate =
        MyContract::new(Bech32ContractId::default(), predicate).methods();

    let call_handler_1 = contract_methods_wallet.initialize_counter(42);
    let call_handler_2 = contract_methods_predicate.get_array([42; 2]);

    let _multi_call_handler = CallHandler::new_multi_call(wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    Ok(())
}

#[tokio::test]
async fn low_level_call() -> Result<()> {
    use fuels::types::SizedAsciiString;

    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyCallerContract",
                project = "e2e/sway/contracts/low_level_caller"
            ),
            Contract(
                name = "MyTargetContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "caller_contract_instance",
            contract = "MyCallerContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "target_contract_instance",
            contract = "MyTargetContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let function_selector = encode_fn_selector("initialize_counter");
    let call_data = calldata!(42u64)?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let response = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await?;
    assert_eq!(response.value, 42);

    let function_selector = encode_fn_selector("set_value_multiple_complex");
    let call_data = calldata!(
        MyStruct {
            a: true,
            b: [1, 2, 3],
        },
        SizedAsciiString::<4>::try_from("fuel")?
    )?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let result_uint = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await
        .unwrap()
        .value;

    let result_bool = target_contract_instance
        .methods()
        .get_bool_value()
        .call()
        .await
        .unwrap()
        .value;

    let result_str = target_contract_instance
        .methods()
        .get_str_value()
        .call()
        .await
        .unwrap()
        .value;

    assert_eq!(result_uint, 42);
    assert!(result_bool);
    assert_eq!(result_str, "fuel");

    Ok(())
}

#[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
#[test]
fn db_rocksdb() {
    use std::{fs, str::FromStr};

    use fuels::{
        accounts::wallet::WalletUnlocked,
        client::{PageDirection, PaginationRequest},
        crypto::SecretKey,
        prelude::{setup_test_provider, DbType, Error, ViewOnlyAccount, DEFAULT_COIN_AMOUNT},
    };

    let temp_dir = tempfile::tempdir().expect("failed to make tempdir");
    let temp_dir_name = temp_dir
        .path()
        .file_name()
        .expect("failed to get file name")
        .to_string_lossy()
        .to_string();
    let temp_database_path = temp_dir.path().join("db");

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let _ = temp_dir;
            let wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            const NUMBER_OF_ASSETS: u64 = 2;
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let chain_config = ChainConfig {
                chain_name: temp_dir_name.clone(),
                consensus_parameters: Default::default(),
                ..ChainConfig::local_testnet()
            };

            let (coins, _) = setup_multiple_assets_coins(
                wallet.address(),
                NUMBER_OF_ASSETS,
                DEFAULT_NUM_COINS,
                DEFAULT_COIN_AMOUNT,
            );

            let provider =
                setup_test_provider(coins.clone(), vec![], Some(node_config), Some(chain_config))
                    .await?;

            provider.produce_blocks(2, None).await?;

            Ok::<(), Error>(())
        })
        .unwrap();

    // The runtime needs to be terminated because the node can currently only be killed when the runtime itself shuts down.

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let provider = setup_test_provider(vec![], vec![], Some(node_config), None).await?;
            // the same wallet that was used when rocksdb was built. When we connect it to the provider, we expect it to have the same amount of assets
            let mut wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            wallet.set_provider(provider.clone());

            let blocks = provider
                .get_blocks(PaginationRequest {
                    cursor: None,
                    results: 10,
                    direction: PageDirection::Forward,
                })
                .await?
                .results;

            assert_eq!(blocks.len(), 3);
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(wallet.get_balances().await?.len(), 2);

            fs::remove_dir_all(
                temp_database_path
                    .parent()
                    .expect("db parent folder does not exist"),
            )?;

            Ok::<(), Error>(())
        })
        .unwrap();
}

#[tokio::test]
async fn can_configure_decoding_of_contract_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: Will not work if max_tokens not big enough
        methods.i_return_a_1k_el_array().with_decoder_config(DecoderConfig{max_tokens: 100, ..Default::default()}).call().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Single call: Works when limit is bumped
        let result = methods
            .i_return_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(result, [0; 1000]);
    }
    {
        // Multi call: Will not work if max_tokens not big enough
        CallHandler::new_multi_call(wallet.clone())
         .add_call(methods.i_return_a_1k_el_array())
         .with_decoder_config(DecoderConfig { max_tokens: 100, ..Default::default() })
         .call::<([u8; 1000],)>().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Multi call: Works when configured
        CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_return_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<([u8; 1000],)>()
            .await
            .unwrap();
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let submitted_tx = contract_methods.get(1, 2).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;

    assert_eq!(value, 3);

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let handle = multi_call_handler.submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let (val_1, val_2): (u64, u64) = handle.response().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_heap_type_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vector_output"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        let call_handler_1 = contract_instance.methods().u8_in_vec(5);
        let call_handler_2 = contract_instance_2.methods().get_single(7);
        let call_handler_3 = contract_instance.methods().u8_in_vec(3);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2)
            .add_call(call_handler_3);

        let (val_1, val_2, val_3): (Vec<u8>, u64, Vec<u8>) = multi_call_handler.call().await?.value;

        assert_eq!(val_1, vec![0, 1, 2, 3, 4]);
        assert_eq!(val_2, 7);
        assert_eq!(val_3, vec![0, 1, 2]);
    }

    Ok(())
}

#[tokio::test]
async fn heap_types_correctly_offset_in_create_transactions_w_storage_slots() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Predicate(
            name = "MyPredicate",
            project = "e2e/sway/types/predicates/predicate_vector"
        ),),
    );

    let provider = wallet.try_provider()?.clone();
    let data = MyPredicateEncoder::default().encode_data(18, 24, vec![2, 4, 42])?;
    let predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(data)
    .with_provider(provider);

    wallet
        .transfer(
            predicate.address(),
            10_000,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await?;

    // if the contract is successfully deployed then the predicate was unlocked. This further means
    // the offsets were setup correctly since the predicate uses heap types in its arguments.
    // Storage slots were loaded automatically by default
    Contract::load_from(
        "sway/contracts/storage/out/release/storage.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    Ok(())
}

#[tokio::test]
async fn test_arguments_with_gas_forwarded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vectors"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let x = 128;
    let vec_input = vec![0, 1, 2];
    {
        let response = contract_instance
            .methods()
            .get_single(x)
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;

        assert_eq!(response.value, x);
    }
    {
        contract_instance_2
            .methods()
            .u32_vec(vec_input.clone())
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;
    }
    {
        let call_handler_1 = contract_instance.methods().get_single(x);
        let call_handler_2 = contract_instance_2.methods().u32_vec(vec_input);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let (value, _): (u64, ()) = multi_call_handler.call().await?.value;

        assert_eq!(value, x);
    }

    Ok(())
}

#[tokio::test]
async fn contract_custom_call_no_signatures_strategy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let provider = wallet.try_provider()?;

    let counter = 42;
    let call_handler = contract_instance.methods().initialize_counter(counter);

    let mut tb = call_handler.transaction_builder().await?;

    let base_asset_id = *provider.consensus_parameters().await?.base_asset_id();

    let amount = 10;
    let consensus_parameters = provider.consensus_parameters().await?;
    let new_base_inputs = wallet
        .get_asset_inputs_for_amount(base_asset_id, amount, None)
        .await?;
    tb.inputs_mut().extend(new_base_inputs);
    tb.outputs_mut()
        .push(Output::change(wallet.address().into(), 0, base_asset_id));

    // ANCHOR: tb_no_signatures_strategy
    let mut tx = tb
        .with_build_strategy(ScriptBuildStrategy::NoSignatures)
        .build(provider)
        .await?;
    // ANCHOR: tx_sign_with
    tx.sign_with(&wallet, consensus_parameters.chain_id())
        .await?;
    // ANCHOR_END: tx_sign_with
    // ANCHOR_END: tb_no_signatures_strategy

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;

    let tx_status = provider.tx_status(&tx_id).await?;

    let response = call_handler.get_response_from(tx_status)?;

    assert_eq!(counter, response.value);

    Ok(())
}

#[tokio::test]
async fn contract_encoder_config_is_applied() -> Result<()> {
    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Wallets("wallet")
    );
    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let instance = TestContract::new(contract_id.clone(), wallet.clone());

    {
        let _encoding_ok = instance
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let instance_with_encoder_config = instance.with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));

        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));
    }

    Ok(())
}

#[tokio::test]
async fn test_reentrant_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LibContractCaller",
            project = "e2e/sway/contracts/lib_contract_caller"
        ),),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = contract_caller_instance.contract_id();
    let response = contract_caller_instance
        .methods()
        .re_entrant(contract_id, true)
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn msg_sender_gas_estimation_issue() {
    // Gas estimation requires an input of the base asset. If absent, a fake input is
    // added. However, if a non-base coin is present and the fake input introduces a
    // second owner, it causes the `msg_sender` sway fn to fail. This leads
    // to a premature failure in gas estimation, risking transaction failure due to
    // a low gas limit.
    let mut wallet = WalletUnlocked::new_random(None);

    let (coins, ids) =
        setup_multiple_assets_coins(wallet.address(), 2, DEFAULT_NUM_COINS, DEFAULT_COIN_AMOUNT);

    let provider = setup_test_provider(coins, vec![], None, None)
        .await
        .unwrap();
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/msg_methods"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let asset_id = ids[0];

    // The fake coin won't be added if we add a base asset, so let's not do that
    assert!(
        asset_id
            != *provider
                .consensus_parameters()
                .await
                .unwrap()
                .base_asset_id()
    );
    let call_params = CallParameters::default()
        .with_amount(100)
        .with_asset_id(asset_id);

    contract_instance
        .methods()
        .message_sender()
        .call_params(call_params)
        .unwrap()
        .call()
        .await
        .unwrap();
}

#[tokio::test]
async fn variable_output_estimation_is_optimized() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/var_outputs"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_methods = contract_instance.methods();

    let coins = 252;
    let recipient = Identity::Address(wallet.address().into());
    let start = Instant::now();
    let _ = contract_methods
        .mint(coins, recipient)
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    // debug builds are slower (20x for `fuel-core-lib`, 4x for a release-fuel-core-binary)
    // we won't validate in that case so we don't have to maintain two expectations
    if !cfg!(debug_assertions) {
        let elapsed = start.elapsed().as_secs();
        let limit = 2;
        if elapsed > limit {
            panic!("Estimation took too long ({elapsed}). Limit is {limit}");
        }
    }

    Ok(())
}

async fn setup_node_with_high_price() -> Result<Vec<WalletUnlocked>> {
    let wallet_config = WalletsConfig::new(None, None, None);
    let fee_parameters = FeeParameters::V1(FeeParametersV1 {
        gas_price_factor: 92000,
        gas_per_byte: 63,
    });
    let consensus_parameters = ConsensusParameters::V1(ConsensusParametersV1 {
        fee_params: fee_parameters,
        ..Default::default()
    });
    let node_config = Some(NodeConfig {
        starting_gas_price: 1100,
        ..NodeConfig::default()
    });
    let chain_config = ChainConfig {
        consensus_parameters,
        ..ChainConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(wallet_config, node_config, Some(chain_config))
            .await?;

    Ok(wallets)
}

#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);

    let response = MyContract::new(contract_id, no_funds_wallet.clone())
        .methods()
        .get(5, 6)
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn simulations_can_be_made_without_coins_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let contract_instance = MyContract::new(contract_id, no_funds_wallet.clone());

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.get(1, 2);
    let call_handler_2 = contract_methods.get(3, 4);

    let mut multi_call_handler = CallHandler::new_multi_call(no_funds_wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let value: (u64, u64) = multi_call_handler
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value, (3, 7));

    Ok(())
}

#[tokio::test]
async fn contract_call_with_non_zero_base_asset_id_and_tip() -> Result<()> {
    use fuels::{prelude::*, tx::ConsensusParameters};

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let asset_id = AssetId::new([1; 32]);

    let mut consensus_parameters = ConsensusParameters::default();
    consensus_parameters.set_base_asset_id(asset_id);

    let config = ChainConfig {
        consensus_parameters,
        ..Default::default()
    };

    let asset_base = AssetConfig {
        id: asset_id,
        num_coins: 1,
        coin_amount: 10_000,
    };

    let wallet_config = WalletsConfig::new_multiple_assets(1, vec![asset_base]);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, Some(config)).await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_tip(10))
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn max_fee_estimation_respects_tolerance() -> Result<()> {
    use fuels::prelude::*;

    let mut call_wallet = WalletUnlocked::new_random(None);

    let call_coins = setup_single_asset_coins(call_wallet.address(), AssetId::BASE, 1000, 1);

    let mut deploy_wallet = WalletUnlocked::new_random(None);
    let deploy_coins =
        setup_single_asset_coins(deploy_wallet.address(), AssetId::BASE, 1, 1_000_000);

    let provider =
        setup_test_provider([call_coins, deploy_coins].concat(), vec![], None, None).await?;

    call_wallet.set_provider(provider.clone());
    deploy_wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            wallet = "deploy_wallet",
            contract = "MyContract",
            random_salt = false,
        )
    );
    let contract_instance = contract_instance.with_account(call_wallet.clone());

    let max_fee_from_tx = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            let builder = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap();

            assert_eq!(
                builder.max_fee_estimation_tolerance, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
                "Expected pre-set tolerance"
            );

            builder
                .with_max_fee_estimation_tolerance(tolerance)
                .build(&provider)
                .await
                .unwrap()
                .max_fee()
                .unwrap()
        }
    };

    let max_fee_from_builder = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance)
                .estimate_max_fee(&provider)
                .await
                .unwrap()
        }
    };

    let base_amount_in_inputs = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let call_wallet = &call_wallet;
        async move {
            let mut tb = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance);

            call_wallet.adjust_for_fee(&mut tb, 0).await.unwrap();
            tb.inputs
                .iter()
                .filter_map(|input: &Input| match input {
                    Input::ResourceSigned { resource }
                        if resource.coin_asset_id().unwrap() == AssetId::BASE =>
                    {
                        Some(resource.amount())
                    }
                    _ => None,
                })
                .sum::<u64>()
        }
    };

    let no_increase_max_fee = max_fee_from_tx(0.0).await;
    let increased_max_fee = max_fee_from_tx(2.00).await;

    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let no_increase_max_fee = max_fee_from_builder(0.0).await;
    let increased_max_fee = max_fee_from_builder(2.00).await;
    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let normal_base_asset = base_amount_in_inputs(0.0).await;
    let more_base_asset_due_to_bigger_tolerance = base_amount_in_inputs(5.00).await;
    assert!(more_base_asset_due_to_bigger_tolerance > normal_base_asset);

    Ok(())
}

#[tokio::test]
async fn blob_contract_deployment() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
    ));

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract_size = std::fs::metadata(contract_binary)
        .expect("contract file not found")
        .len();

    assert!(
         contract_size > 150_000,
         "the testnet size limit was around 100kB, we want a contract bigger than that to reflect prod (current: {contract_size}B)"
     );

    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::new(Some(2), None, None), None, None)
            .await?;

    let provider = wallets[0].provider().unwrap().clone();

    let consensus_parameters = provider.consensus_parameters().await?;

    let contract_max_size = consensus_parameters.contract_params().contract_max_size();
    assert!(
         contract_size > contract_max_size,
         "this test should ideally be run with a contract bigger than the max contract size ({contract_max_size}B) so that we know deployment couldn't have happened without blobs"
     );

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallets[0], TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn regular_contract_can_be_deployed() -> Result<()> {
    // given
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
    );

    let contract_binary = "sway/contracts/contract_test/out/release/contract_test.bin";

    // when
    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    // then
    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance
        .methods()
        .get_counter()
        .call()
        .await?
        .value;

    assert_eq!(response, 0);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_be_deployed_directly() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_upload_blobs_separately_then_deploy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    let blob_ids = contract.blob_ids();

    // if this were an example for the user we'd just call `deploy` on the contract above
    // this way we are testing that the blobs were really deployed above, otherwise the following
    // would fail
    let contract_id = Contract::loader_from_blob_ids(
        blob_ids.to_vec(),
        contract.salt(),
        contract.storage_slots().to_vec(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_blob_already_uploaded_not_an_issue() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?;

    // this will upload blobs
    contract
        .clone()
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    // this will try to upload the blobs but skip upon encountering an error
    let contract_id = contract
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .something()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_storage_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_storage_slots = contract.storage_slots().to_vec();

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";
    let proxy_contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let combined_storage_slots = [&contract_storage_slots, proxy_contract.storage_slots()].concat();

    let proxy_id = proxy_contract
        .with_storage_slots(combined_storage_slots)
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?
        .value;

    assert_eq!(response, 42);

    let _res = proxy
        .methods()
        .write_some_u64(36)
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 36);

    Ok(())
}\n```

Multiple contract calls

With CallHandler, you can execute multiple contract calls within a single transaction. To achieve this, you first prepare all the contract calls that you want to bundle:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

You can also set call parameters, variable outputs, or external contracts for every contract call, as long as you don't execute it with call() or simulate().

Next, you provide the prepared calls to your CallHandler and optionally configure transaction policies:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Note: any transaction policies configured on separate contract calls are disregarded in favor of the parameters provided to the multi-call CallHandler.

Furthermore, if you need to separate submission from value retrieval for any reason, you can do so as follows:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Output values

To get the output values of the bundled calls, you need to provide explicit type annotations when saving the result of call() or simulate() to a variable:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

You can also interact with the CallResponse by moving the type annotation to the invoked method:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Transaction dependency estimation

Previously, we mentioned that a contract call might require you to manually specify external contracts, variable outputs, or output messages. The SDK can also attempt to estimate and set these dependencies for you at the cost of running multiple simulated calls in the background.

The following example uses a contract call that calls an external contract and later mints assets to a specified address. Calling it without including the dependencies will result in a revert:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

As mentioned in previous chapters, you can specify the external contract and add an output variable to resolve this:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

But this requires you to know the contract ID of the external contract and the needed number of output variables. Alternatively, by chaining .estimate_tx_dependencies() instead, the dependencies will be estimated by the SDK and set automatically. The optional parameter is the maximum number of simulation attempts:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

The minimal number of attempts corresponds to the number of external contracts and output variables needed and defaults to 10.

Note: estimate_tx_dependencies() can also be used when working with script calls or multi calls. estimate_tx_dependencies() does not currently resolve the dependencies needed for logging from an external contract. For more information, see here. If no resolution was found after exhausting all simulation attempts, the last received error will be propagated. The same will happen if an error is unrelated to transaction dependencies.

Estimating contract call cost

With the function estimate_transaction_cost(tolerance: Option<f64>, block_horizon: Option<u32>) provided by CallHandler, you can get a cost estimation for a specific call. The return type, TransactionCost, is a struct that contains relevant information for the estimation:

```rust\n#[cfg(feature = "coin-cache")]
use std::sync::Arc;
use std::{collections::HashMap, fmt::Debug, net::SocketAddr};

mod cache;
mod retry_util;
mod retryable_client;
mod supported_fuel_core_version;
mod supported_versions;

use crate::provider::cache::CacheableRpcs;
pub use cache::TtlConfig;
use cache::{CachedClient, SystemClock};
use chrono::{DateTime, Utc};
use fuel_core_client::client::{
    pagination::{PageDirection, PaginatedResult, PaginationRequest},
    types::{
        balance::Balance,
        contract::ContractBalance,
        gas_price::{EstimateGasPrice, LatestGasPrice},
    },
};
use fuel_core_types::services::executor::TransactionExecutionResult;
use fuel_tx::{
    AssetId, ConsensusParameters, Receipt, Transaction as FuelTransaction, TxId, UtxoId,
};
use fuel_types::{Address, BlockHeight, Bytes32, Nonce};
#[cfg(feature = "coin-cache")]
use fuels_core::types::coin_type_id::CoinTypeId;
use fuels_core::{
    constants::{DEFAULT_GAS_ESTIMATION_BLOCK_HORIZON, DEFAULT_GAS_ESTIMATION_TOLERANCE},
    types::{
        bech32::{Bech32Address, Bech32ContractId},
        block::{Block, Header},
        chain_info::ChainInfo,
        coin::Coin,
        coin_type::CoinType,
        errors::Result,
        message::Message,
        message_proof::MessageProof,
        node_info::NodeInfo,
        transaction::{Transaction, Transactions},
        transaction_builders::{Blob, BlobId},
        transaction_response::TransactionResponse,
        tx_status::TxStatus,
        DryRun, DryRunner,
    },
};
pub use retry_util::{Backoff, RetryConfig};
pub use supported_fuel_core_version::SUPPORTED_FUEL_CORE_VERSION;
use tai64::Tai64;
#[cfg(feature = "coin-cache")]
use tokio::sync::Mutex;

#[cfg(feature = "coin-cache")]
use crate::coin_cache::CoinsCache;
use crate::provider::retryable_client::RetryableClient;

const NUM_RESULTS_PER_REQUEST: i32 = 100;

#[derive(Debug, Clone, PartialEq)]
// ANCHOR: transaction_cost
pub struct TransactionCost {
    pub gas_price: u64,
    pub gas_used: u64,
    pub metered_bytes_size: u64,
    pub total_fee: u64,
}
// ANCHOR_END: transaction_cost

pub(crate) struct ResourceQueries {
    utxos: Vec<UtxoId>,
    messages: Vec<Nonce>,
    asset_id: Option<AssetId>,
    amount: u64,
}

impl ResourceQueries {
    pub fn exclusion_query(&self) -> Option<(Vec<UtxoId>, Vec<Nonce>)> {
        if self.utxos.is_empty() && self.messages.is_empty() {
            return None;
        }

        Some((self.utxos.clone(), self.messages.clone()))
    }

    pub fn spend_query(&self, base_asset_id: AssetId) -> Vec<(AssetId, u64, Option<u32>)> {
        vec![(self.asset_id.unwrap_or(base_asset_id), self.amount, None)]
    }
}

#[derive(Default)]
// ANCHOR: resource_filter
pub struct ResourceFilter {
    pub from: Bech32Address,
    pub asset_id: Option<AssetId>,
    pub amount: u64,
    pub excluded_utxos: Vec<UtxoId>,
    pub excluded_message_nonces: Vec<Nonce>,
}
// ANCHOR_END: resource_filter

impl ResourceFilter {
    pub fn owner(&self) -> Address {
        (&self.from).into()
    }

    pub(crate) fn resource_queries(&self) -> ResourceQueries {
        ResourceQueries {
            utxos: self.excluded_utxos.clone(),
            messages: self.excluded_message_nonces.clone(),
            asset_id: self.asset_id,
            amount: self.amount,
        }
    }
}

/// Encapsulates common client operations in the SDK.
/// Note that you may also use `client`, which is an instance
/// of `FuelClient`, directly, which provides a broader API.
#[derive(Debug, Clone)]
pub struct Provider {
    cached_client: CachedClient<RetryableClient>,
    #[cfg(feature = "coin-cache")]
    coins_cache: Arc<Mutex<CoinsCache>>,
}

impl Provider {
    pub async fn from(addr: impl Into<SocketAddr>) -> Result<Self> {
        let addr = addr.into();
        Self::connect(format!("http://{addr}")).await
    }

    pub fn set_cache_ttl(&mut self, ttl: TtlConfig) {
        self.cached_client.set_ttl(ttl);
    }

    pub async fn clear_cache(&self) {
        self.cached_client.clear().await;
    }

    pub async fn healthy(&self) -> Result<bool> {
        Ok(self.uncached_client().health().await?)
    }

    /// Connects to an existing node at the given address.
    pub async fn connect(url: impl AsRef<str>) -> Result<Provider> {
        let client = CachedClient::new(
            RetryableClient::connect(&url, Default::default()).await?,
            TtlConfig::default(),
            SystemClock,
        );

        Ok(Self {
            cached_client: client,
            #[cfg(feature = "coin-cache")]
            coins_cache: Default::default(),
        })
    }

    pub fn url(&self) -> &str {
        self.uncached_client().url()
    }

    pub async fn blob(&self, blob_id: BlobId) -> Result<Option<Blob>> {
        Ok(self
            .uncached_client()
            .blob(blob_id.into())
            .await?
            .map(|blob| Blob::new(blob.bytecode)))
    }

    pub async fn blob_exists(&self, blob_id: BlobId) -> Result<bool> {
        Ok(self.uncached_client().blob_exists(blob_id.into()).await?)
    }

    /// Sends a transaction to the underlying Provider's client.
    pub async fn send_transaction_and_await_commit<T: Transaction>(
        &self,
        tx: T,
    ) -> Result<TxStatus> {
        #[cfg(feature = "coin-cache")]
        let base_asset_id = *self.consensus_parameters().await?.base_asset_id();

        #[cfg(feature = "coin-cache")]
        self.check_inputs_already_in_cache(&tx.used_coins(&base_asset_id))
            .await?;

        let tx = self.prepare_transaction_for_sending(tx).await?;
        let tx_status = self
            .uncached_client()
            .submit_and_await_commit(&tx.clone().into())
            .await?
            .into();

        #[cfg(feature = "coin-cache")]
        if matches!(
            tx_status,
            TxStatus::SqueezedOut { .. } | TxStatus::Revert { .. }
        ) {
            self.coins_cache
                .lock()
                .await
                .remove_items(tx.used_coins(&base_asset_id))
        }

        Ok(tx_status)
    }

    async fn prepare_transaction_for_sending<T: Transaction>(&self, mut tx: T) -> Result<T> {
        let consensus_parameters = self.consensus_parameters().await?;
        tx.precompute(&consensus_parameters.chain_id())?;

        let chain_info = self.chain_info().await?;
        let Header {
            height: latest_block_height,
            state_transition_bytecode_version: latest_chain_executor_version,
            ..
        } = chain_info.latest_block.header;

        if tx.is_using_predicates() {
            tx.estimate_predicates(self, Some(latest_chain_executor_version))
                .await?;
            tx.clone()
                .validate_predicates(&consensus_parameters, latest_block_height)?;
        }

        self.validate_transaction(tx.clone()).await?;

        Ok(tx)
    }

    pub async fn send_transaction<T: Transaction>(&self, tx: T) -> Result<TxId> {
        let tx = self.prepare_transaction_for_sending(tx).await?;
        self.submit(tx).await
    }

    pub async fn await_transaction_commit<T: Transaction>(&self, id: TxId) -> Result<TxStatus> {
        Ok(self
            .uncached_client()
            .await_transaction_commit(&id)
            .await?
            .into())
    }

    async fn validate_transaction<T: Transaction>(&self, tx: T) -> Result<()> {
        let tolerance = 0.0;
        let TransactionCost { gas_used, .. } = self
            .estimate_transaction_cost(tx.clone(), Some(tolerance), None)
            .await?;

        tx.validate_gas(gas_used)?;

        Ok(())
    }

    #[cfg(not(feature = "coin-cache"))]
    async fn submit<T: Transaction>(&self, tx: T) -> Result<TxId> {
        Ok(self.uncached_client().submit(&tx.into()).await?)
    }

    #[cfg(feature = "coin-cache")]
    async fn find_in_cache<'a>(
        &self,
        coin_ids: impl IntoIterator<Item = (&'a (Bech32Address, AssetId), &'a Vec<CoinTypeId>)>,
    ) -> Option<((Bech32Address, AssetId), CoinTypeId)> {
        let mut locked_cache = self.coins_cache.lock().await;

        for (key, ids) in coin_ids {
            let items = locked_cache.get_active(key);

            if items.is_empty() {
                continue;
            }

            for id in ids {
                if items.contains(id) {
                    return Some((key.clone(), id.clone()));
                }
            }
        }

        None
    }

    #[cfg(feature = "coin-cache")]
    async fn check_inputs_already_in_cache<'a>(
        &self,
        coin_ids: impl IntoIterator<Item = (&'a (Bech32Address, AssetId), &'a Vec<CoinTypeId>)>,
    ) -> Result<()> {
        use fuels_core::types::errors::{transaction, Error};

        if let Some(((addr, asset_id), coin_type_id)) = self.find_in_cache(coin_ids).await {
            let msg = match coin_type_id {
                CoinTypeId::UtxoId(utxo_id) => format!("coin with utxo_id: `{utxo_id:x}`"),
                CoinTypeId::Nonce(nonce) => format!("message with nonce: `{nonce}`"),
            };
            Err(Error::Transaction(transaction::Reason::Validation(
                format!("{msg} was submitted recently in a transaction - attempting to spend it again will result in an error. Wallet address: `{addr}`, asset id: `{asset_id}`"),
            )))
        } else {
            Ok(())
        }
    }

    #[cfg(feature = "coin-cache")]
    async fn submit<T: Transaction>(&self, tx: T) -> Result<TxId> {
        let consensus_parameters = self.consensus_parameters().await?;
        let base_asset_id = consensus_parameters.base_asset_id();

        let used_utxos = tx.used_coins(base_asset_id);
        self.check_inputs_already_in_cache(&used_utxos).await?;

        let tx_id = self.uncached_client().submit(&tx.into()).await?;
        self.coins_cache.lock().await.insert_multiple(used_utxos);

        Ok(tx_id)
    }

    pub async fn tx_status(&self, tx_id: &TxId) -> Result<TxStatus> {
        Ok(self
            .uncached_client()
            .transaction_status(tx_id)
            .await?
            .into())
    }

    pub async fn chain_info(&self) -> Result<ChainInfo> {
        Ok(self.uncached_client().chain_info().await?.into())
    }

    pub async fn consensus_parameters(&self) -> Result<ConsensusParameters> {
        self.cached_client.consensus_parameters().await
    }

    pub async fn node_info(&self) -> Result<NodeInfo> {
        Ok(self.uncached_client().node_info().await?.into())
    }

    pub async fn latest_gas_price(&self) -> Result<LatestGasPrice> {
        Ok(self.uncached_client().latest_gas_price().await?)
    }

    pub async fn estimate_gas_price(&self, block_horizon: u32) -> Result<EstimateGasPrice> {
        Ok(self
            .uncached_client()
            .estimate_gas_price(block_horizon)
            .await?)
    }

    pub async fn dry_run(&self, tx: impl Transaction) -> Result<TxStatus> {
        let [tx_status] = self
            .uncached_client()
            .dry_run(Transactions::new().insert(tx).as_slice())
            .await?
            .into_iter()
            .map(Into::into)
            .collect::<Vec<_>>()
            .try_into()
            .expect("should have only one element");

        Ok(tx_status)
    }

    pub async fn dry_run_multiple(
        &self,
        transactions: Transactions,
    ) -> Result<Vec<(TxId, TxStatus)>> {
        Ok(self
            .uncached_client()
            .dry_run(transactions.as_slice())
            .await?
            .into_iter()
            .map(|execution_status| (execution_status.id, execution_status.into()))
            .collect())
    }

    pub async fn dry_run_opt(
        &self,
        tx: impl Transaction,
        utxo_validation: bool,
        gas_price: Option<u64>,
    ) -> Result<TxStatus> {
        let [tx_status] = self
            .uncached_client()
            .dry_run_opt(
                Transactions::new().insert(tx).as_slice(),
                Some(utxo_validation),
                gas_price,
            )
            .await?
            .into_iter()
            .map(Into::into)
            .collect::<Vec<_>>()
            .try_into()
            .expect("should have only one element");

        Ok(tx_status)
    }

    pub async fn dry_run_opt_multiple(
        &self,
        transactions: Transactions,
        utxo_validation: bool,
        gas_price: Option<u64>,
    ) -> Result<Vec<(TxId, TxStatus)>> {
        Ok(self
            .uncached_client()
            .dry_run_opt(transactions.as_slice(), Some(utxo_validation), gas_price)
            .await?
            .into_iter()
            .map(|execution_status| (execution_status.id, execution_status.into()))
            .collect())
    }

    /// Gets all unspent coins owned by address `from`, with asset ID `asset_id`.
    pub async fn get_coins(&self, from: &Bech32Address, asset_id: AssetId) -> Result<Vec<Coin>> {
        let mut coins: Vec<Coin> = vec![];
        let mut cursor = None;

        loop {
            let response = self
                .uncached_client()
                .coins(
                    &from.into(),
                    Some(&asset_id),
                    PaginationRequest {
                        cursor: cursor.clone(),
                        results: NUM_RESULTS_PER_REQUEST,
                        direction: PageDirection::Forward,
                    },
                )
                .await?;

            if response.results.is_empty() {
                break;
            }

            coins.extend(response.results.into_iter().map(Into::into));
            cursor = response.cursor;
        }

        Ok(coins)
    }

    async fn request_coins_to_spend(&self, filter: ResourceFilter) -> Result<Vec<CoinType>> {
        let queries = filter.resource_queries();

        let consensus_parameters = self.consensus_parameters().await?;
        let base_asset_id = *consensus_parameters.base_asset_id();

        let res = self
            .uncached_client()
            .coins_to_spend(
                &filter.owner(),
                queries.spend_query(base_asset_id),
                queries.exclusion_query(),
            )
            .await?
            .into_iter()
            .flatten()
            .map(CoinType::from)
            .collect();

        Ok(res)
    }

    /// Get some spendable coins of asset `asset_id` for address `from` that add up at least to
    /// amount `amount`. The returned coins (UTXOs) are actual coins that can be spent. The number
    /// of coins (UXTOs) is optimized to prevent dust accumulation.
    #[cfg(not(feature = "coin-cache"))]
    pub async fn get_spendable_resources(&self, filter: ResourceFilter) -> Result<Vec<CoinType>> {
        self.request_coins_to_spend(filter).await
    }

    /// Get some spendable coins of asset `asset_id` for address `from` that add up at least to
    /// amount `amount`. The returned coins (UTXOs) are actual coins that can be spent. The number
    /// of coins (UXTOs) is optimized to prevent dust accumulation.
    /// Coins that were recently submitted inside a tx will be ignored from the results.
    #[cfg(feature = "coin-cache")]
    pub async fn get_spendable_resources(
        &self,
        mut filter: ResourceFilter,
    ) -> Result<Vec<CoinType>> {
        self.extend_filter_with_cached(&mut filter).await?;

        self.request_coins_to_spend(filter).await
    }

    #[cfg(feature = "coin-cache")]
    async fn extend_filter_with_cached(&self, filter: &mut ResourceFilter) -> Result<()> {
        let consensus_parameters = self.consensus_parameters().await?;
        let mut cache = self.coins_cache.lock().await;
        let asset_id = filter
            .asset_id
            .unwrap_or(*consensus_parameters.base_asset_id());
        let used_coins = cache.get_active(&(filter.from.clone(), asset_id));

        let excluded_utxos = used_coins
            .iter()
            .filter_map(|coin_id| match coin_id {
                CoinTypeId::UtxoId(utxo_id) => Some(utxo_id),
                _ => None,
            })
            .cloned()
            .collect::<Vec<_>>();

        let excluded_message_nonces = used_coins
            .iter()
            .filter_map(|coin_id| match coin_id {
                CoinTypeId::Nonce(nonce) => Some(nonce),
                _ => None,
            })
            .cloned()
            .collect::<Vec<_>>();

        filter.excluded_utxos.extend(excluded_utxos);
        filter
            .excluded_message_nonces
            .extend(excluded_message_nonces);

        Ok(())
    }

    /// Get the balance of all spendable coins `asset_id` for address `address`. This is different
    /// from getting coins because we are just returning a number (the sum of UTXOs amount) instead
    /// of the UTXOs.
    pub async fn get_asset_balance(
        &self,
        address: &Bech32Address,
        asset_id: AssetId,
    ) -> Result<u64> {
        Ok(self
            .uncached_client()
            .balance(&address.into(), Some(&asset_id))
            .await?)
    }

    /// Get the balance of all spendable coins `asset_id` for contract with id `contract_id`.
    pub async fn get_contract_asset_balance(
        &self,
        contract_id: &Bech32ContractId,
        asset_id: AssetId,
    ) -> Result<u64> {
        Ok(self
            .uncached_client()
            .contract_balance(&contract_id.into(), Some(&asset_id))
            .await?)
    }

    /// Get all the spendable balances of all assets for address `address`. This is different from
    /// getting the coins because we are only returning the numbers (the sum of UTXOs coins amount
    /// for each asset id) and not the UTXOs coins themselves
    pub async fn get_balances(&self, address: &Bech32Address) -> Result<HashMap<String, u128>> {
        let mut balances = HashMap::new();
        let mut cursor = None;

        loop {
            let response = self
                .uncached_client()
                .balances(
                    &address.into(),
                    PaginationRequest {
                        cursor: cursor.clone(),
                        results: NUM_RESULTS_PER_REQUEST,
                        direction: PageDirection::Forward,
                    },
                )
                .await?;

            if response.results.is_empty() {
                break;
            }

            balances.extend(response.results.into_iter().map(
                |Balance {
                     owner: _,
                     amount,
                     asset_id,
                 }| (asset_id.to_string(), amount),
            ));
            cursor = response.cursor;
        }

        Ok(balances)
    }

    /// Get all balances of all assets for the contract with id `contract_id`.
    pub async fn get_contract_balances(
        &self,
        contract_id: &Bech32ContractId,
    ) -> Result<HashMap<AssetId, u64>> {
        let mut contract_balances = HashMap::new();
        let mut cursor = None;

        loop {
            let response = self
                .uncached_client()
                .contract_balances(
                    &contract_id.into(),
                    PaginationRequest {
                        cursor: cursor.clone(),
                        results: NUM_RESULTS_PER_REQUEST,
                        direction: PageDirection::Forward,
                    },
                )
                .await?;

            if response.results.is_empty() {
                break;
            }

            contract_balances.extend(response.results.into_iter().map(
                |ContractBalance {
                     contract: _,
                     amount,
                     asset_id,
                 }| (asset_id, amount),
            ));
            cursor = response.cursor;
        }

        Ok(contract_balances)
    }

    pub async fn get_transaction_by_id(&self, tx_id: &TxId) -> Result<Option<TransactionResponse>> {
        Ok(self
            .uncached_client()
            .transaction(tx_id)
            .await?
            .map(Into::into))
    }

    pub async fn get_transactions(
        &self,
        request: PaginationRequest<String>,
    ) -> Result<PaginatedResult<TransactionResponse, String>> {
        let pr = self.uncached_client().transactions(request).await?;

        Ok(PaginatedResult {
            cursor: pr.cursor,
            results: pr.results.into_iter().map(Into::into).collect(),
            has_next_page: pr.has_next_page,
            has_previous_page: pr.has_previous_page,
        })
    }

    // Get transaction(s) by owner
    pub async fn get_transactions_by_owner(
        &self,
        owner: &Bech32Address,
        request: PaginationRequest<String>,
    ) -> Result<PaginatedResult<TransactionResponse, String>> {
        let pr = self
            .uncached_client()
            .transactions_by_owner(&owner.into(), request)
            .await?;

        Ok(PaginatedResult {
            cursor: pr.cursor,
            results: pr.results.into_iter().map(Into::into).collect(),
            has_next_page: pr.has_next_page,
            has_previous_page: pr.has_previous_page,
        })
    }

    pub async fn latest_block_height(&self) -> Result<u32> {
        Ok(self.chain_info().await?.latest_block.header.height)
    }

    pub async fn latest_block_time(&self) -> Result<Option<DateTime<Utc>>> {
        Ok(self.chain_info().await?.latest_block.header.time)
    }

    pub async fn produce_blocks(
        &self,
        blocks_to_produce: u32,
        start_time: Option<DateTime<Utc>>,
    ) -> Result<u32> {
        let start_time = start_time.map(|time| Tai64::from_unix(time.timestamp()).0);

        Ok(self
            .uncached_client()
            .produce_blocks(blocks_to_produce, start_time)
            .await?
            .into())
    }

    pub async fn block(&self, block_id: &Bytes32) -> Result<Option<Block>> {
        Ok(self
            .uncached_client()
            .block(block_id)
            .await?
            .map(Into::into))
    }

    pub async fn block_by_height(&self, height: BlockHeight) -> Result<Option<Block>> {
        Ok(self
            .uncached_client()
            .block_by_height(height)
            .await?
            .map(Into::into))
    }

    // - Get block(s)
    pub async fn get_blocks(
        &self,
        request: PaginationRequest<String>,
    ) -> Result<PaginatedResult<Block, String>> {
        let pr = self.uncached_client().blocks(request).await?;

        Ok(PaginatedResult {
            cursor: pr.cursor,
            results: pr.results.into_iter().map(Into::into).collect(),
            has_next_page: pr.has_next_page,
            has_previous_page: pr.has_previous_page,
        })
    }

    pub async fn estimate_transaction_cost<T: Transaction>(
        &self,
        mut tx: T,
        tolerance: Option<f64>,
        block_horizon: Option<u32>,
    ) -> Result<TransactionCost> {
        let block_horizon = block_horizon.unwrap_or(DEFAULT_GAS_ESTIMATION_BLOCK_HORIZON);
        let tolerance = tolerance.unwrap_or(DEFAULT_GAS_ESTIMATION_TOLERANCE);

        let EstimateGasPrice { gas_price, .. } = self.estimate_gas_price(block_horizon).await?;

        let gas_used = self
            .get_gas_used_with_tolerance(tx.clone(), tolerance)
            .await?;

        if tx.is_using_predicates() {
            tx.estimate_predicates(self, None).await?;
        }

        let transaction_fee = tx
            .clone()
            .fee_checked_from_tx(&self.consensus_parameters().await?, gas_price)
            .expect("Error calculating TransactionFee");

        Ok(TransactionCost {
            gas_price,
            gas_used,
            metered_bytes_size: tx.metered_bytes_size() as u64,
            total_fee: transaction_fee.max_fee(),
        })
    }

    // Increase estimated gas by the provided tolerance
    async fn get_gas_used_with_tolerance<T: Transaction>(
        &self,
        tx: T,
        tolerance: f64,
    ) -> Result<u64> {
        let receipts = self.dry_run_opt(tx, false, None).await?.take_receipts();
        let gas_used = self.get_script_gas_used(&receipts);

        Ok((gas_used as f64 * (1.0 + tolerance)).ceil() as u64)
    }

    fn get_script_gas_used(&self, receipts: &[Receipt]) -> u64 {
        receipts
            .iter()
            .rfind(|r| matches!(r, Receipt::ScriptResult { .. }))
            .map(|script_result| {
                script_result
                    .gas_used()
                    .expect("could not retrieve gas used from ScriptResult")
            })
            .unwrap_or(0)
    }

    pub async fn get_messages(&self, from: &Bech32Address) -> Result<Vec<Message>> {
        let mut messages = Vec::new();
        let mut cursor = None;

        loop {
            let response = self
                .uncached_client()
                .messages(
                    Some(&from.into()),
                    PaginationRequest {
                        cursor: cursor.clone(),
                        results: NUM_RESULTS_PER_REQUEST,
                        direction: PageDirection::Forward,
                    },
                )
                .await?;

            if response.results.is_empty() {
                break;
            }

            messages.extend(response.results.into_iter().map(Into::into));
            cursor = response.cursor;
        }

        Ok(messages)
    }

    pub async fn get_message_proof(
        &self,
        tx_id: &TxId,
        nonce: &Nonce,
        commit_block_id: Option<&Bytes32>,
        commit_block_height: Option<u32>,
    ) -> Result<MessageProof> {
        self.uncached_client()
            .message_proof(
                tx_id,
                nonce,
                commit_block_id.map(Into::into),
                commit_block_height.map(Into::into),
            )
            .await
            .map(Into::into)
            .map_err(Into::into)
    }

    pub async fn is_user_account(&self, address: impl Into<Bytes32>) -> Result<bool> {
        self.uncached_client()
            .is_user_account(*address.into())
            .await
    }

    pub fn with_retry_config(mut self, retry_config: RetryConfig) -> Self {
        self.uncached_client_mut().set_retry_config(retry_config);

        self
    }

    pub async fn contract_exists(&self, contract_id: &Bech32ContractId) -> Result<bool> {
        Ok(self
            .uncached_client()
            .contract_exists(&contract_id.into())
            .await?)
    }

    fn uncached_client(&self) -> &RetryableClient {
        self.cached_client.inner()
    }

    fn uncached_client_mut(&mut self) -> &mut RetryableClient {
        self.cached_client.inner_mut()
    }
}

#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
impl DryRunner for Provider {
    async fn dry_run(&self, tx: FuelTransaction) -> Result<DryRun> {
        let [tx_execution_status] = self
            .uncached_client()
            .dry_run_opt(&vec![tx], Some(false), Some(0))
            .await?
            .try_into()
            .expect("should have only one element");

        let receipts = tx_execution_status.result.receipts();
        let script_gas = self.get_script_gas_used(receipts);

        let variable_outputs = receipts
            .iter()
            .filter(
                |receipt| matches!(receipt, Receipt::TransferOut { amount, .. } if *amount != 0),
            )
            .count();

        let succeeded = matches!(
            tx_execution_status.result,
            TransactionExecutionResult::Success { .. }
        );

        let dry_run = DryRun {
            succeeded,
            script_gas,
            variable_outputs,
        };

        Ok(dry_run)
    }

    async fn estimate_gas_price(&self, block_horizon: u32) -> Result<u64> {
        Ok(self.estimate_gas_price(block_horizon).await?.gas_price)
    }

    async fn estimate_predicates(
        &self,
        tx: &FuelTransaction,
        _latest_chain_executor_version: Option<u32>,
    ) -> Result<FuelTransaction> {
        Ok(self.uncached_client().estimate_predicates(tx).await?)
    }

    async fn consensus_parameters(&self) -> Result<ConsensusParameters> {
        Provider::consensus_parameters(self).await
    }
}\n```

Below are examples that show how to get the estimated transaction cost from single and multi call transactions.

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```
```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

The transaction cost estimation can be used to set the gas limit for an actual call, or to show the user the estimated cost.

Note The same estimation interface is available for scripts.

Low-level calls

With low-level calls, you can specify the parameters of your calls at runtime and make indirect calls through other contracts.

Your caller contract should call std::low_level_call::call_with_function_selector, providing:

  • target contract ID
  • function selector encoded as Bytes
  • calldata encoded as Bytes
  • whether the calldata contains only a single value argument (e.g. a u64)
  • std::low_level_call::CallParams
```sway\ncontract;

use std::{
    bytes::Bytes,
    constants::ZERO_B256,
    low_level_call::{
        call_with_function_selector,
        CallParams,
    },
};

abi MyCallerContract {
    fn call_low_level_call(
        target: ContractId,
        function_selector: Bytes,
        calldata: Bytes,
    );
}

impl MyCallerContract for Contract {
    // ANCHOR: low_level_call_contract
    fn call_low_level_call(
        target: ContractId,
        function_selector: Bytes,
        calldata: Bytes,
    ) {
        let call_params = CallParams {
            coins: 0,
            asset_id: AssetId::from(ZERO_B256),
            gas: 10_000,
        };

        call_with_function_selector(target, function_selector, calldata, call_params);
    }
    // ANCHOR_END: low_level_call_contract
}\n```

On the SDK side, you can construct an encoded function selector using fuels::core::encode_fn_selector, and encoded calldata using the fuels::core::calldata! macro.

E.g. to call the following function on the target contract:

```sway\ncontract;

use std::storage::storage_api::{read, write};
use std::context::msg_amount;

struct MyType {
    x: u64,
    y: u64,
}

#[allow(dead_code)]
struct Person {
    name: str[4],
}

#[allow(dead_code)]
enum State {
    A: (),
    B: (),
    C: (),
}

abi TestContract {
    #[storage(write)]
    fn initialize_counter(value: u64) -> u64;
    #[storage(read, write)]
    fn increment_counter(value: u64) -> u64;
    #[storage(read)]
    fn get_counter() -> u64;
    // ANCHOR: low_level_call
    #[storage(write)]
    fn set_value_multiple_complex(a: MyStruct, b: str[4]);
    // ANCHOR_END: low_level_call
    #[storage(read)]
    fn get_str_value() -> str[4];
    #[storage(read)]
    fn get_bool_value() -> bool;
    #[storage(read)]
    fn get_value() -> u64;
    fn get(x: u64, y: u64) -> u64;
    fn get_alt(x: MyType) -> MyType;
    fn get_single(x: u64) -> u64;
    fn array_of_structs(p: [Person; 2]) -> [Person; 2];
    fn array_of_enums(p: [State; 2]) -> [State; 2];
    fn get_array(p: [u64; 2]) -> [u64; 2];
    #[payable]
    fn get_msg_amount() -> u64;
    fn new() -> u64;
}

const COUNTER_KEY = 0x0000000000000000000000000000000000000000000000000000000000000000;

storage {
    value: u64 = 0,
    value_str: str[4] = __to_str_array("none"),
    value_bool: bool = false,
}

pub struct MyStruct {
    a: bool,
    b: [u64; 3],
}

impl TestContract for Contract {
    // ANCHOR: msg_amount
    #[payable]
    fn get_msg_amount() -> u64 {
        msg_amount()
    }
    // ANCHOR_END: msg_amount
    #[storage(write)]
    fn initialize_counter(value: u64) -> u64 {
        write(COUNTER_KEY, 0, value);
        value
    }

    /// This method will read the counter from storage, increment it
    /// and write the incremented value to storage
    #[storage(read, write)]
    fn increment_counter(value: u64) -> u64 {
        let new_value = read::<u64>(COUNTER_KEY, 0).unwrap_or(0) + value;
        write(COUNTER_KEY, 0, new_value);
        new_value
    }

    #[storage(read)]
    fn get_counter() -> u64 {
        read::<u64>(COUNTER_KEY, 0).unwrap_or(0)
    }

    #[storage(write)]
    fn set_value_multiple_complex(a: MyStruct, b: str[4]) {
        storage.value.write(a.b[1]);
        storage.value_str.write(b);
        storage.value_bool.write(a.a);
    }

    #[storage(read)]
    fn get_str_value() -> str[4] {
        storage.value_str.read()
    }

    #[storage(read)]
    fn get_bool_value() -> bool {
        storage.value_bool.read()
    }

    #[storage(read)]
    fn get_value() -> u64 {
        storage.value.read()
    }

    fn get(x: u64, y: u64) -> u64 {
        x + y
    }

    fn get_alt(t: MyType) -> MyType {
        t
    }

    fn get_single(x: u64) -> u64 {
        x
    }

    fn array_of_structs(p: [Person; 2]) -> [Person; 2] {
        p
    }

    fn array_of_enums(p: [State; 2]) -> [State; 2] {
        p
    }

    fn get_array(p: [u64; 2]) -> [u64; 2] {
        p
    }

    fn new() -> u64 {
        12345u64
    }
}\n```

you would construct the function selector and the calldata as such, and provide them to the caller contract (like the one above):

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Note: the calldata! macro uses the default EncoderConfig configuration under the hood.

Running scripts

You can run a script using its JSON-ABI and the path to its binary file. You can run the scripts with arguments. For this, you have to use the abigen! macro seen previously.

```rust\nuse std::time::Duration;

use fuel_tx::Output;
use fuels::{
    client::{PageDirection, PaginationRequest},
    core::{
        codec::{DecoderConfig, EncoderConfig},
        traits::Tokenizable,
        Configurables,
    },
    prelude::*,
    programs::{executable::Executable, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE},
    types::{Bits256, Identity},
};

#[tokio::test]
async fn main_function_arguments() -> Result<()> {
    // ANCHOR: script_with_arguments
    // The abigen is used for the same purpose as with contracts (Rust bindings)
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/scripts/arguments/out/release/arguments.bin";
    let script_instance = MyScript::new(wallet, bin_path);

    let bim = Bimbam { val: 90 };
    let bam = SugarySnack {
        twix: 100,
        mars: 1000,
    };

    let result = script_instance.main(bim, bam).call().await?;

    let expected = Bimbam { val: 2190 };
    assert_eq!(result.value, expected);
    // ANCHOR_END: script_with_arguments
    Ok(())
}

#[tokio::test]
async fn script_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let a = 4u64;
    let b = 2u32;
    let estimated_gas_used = script_instance
        .main(a, b)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = script_instance.main(a, b).call().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);

    Ok(())
}

#[tokio::test]
async fn test_basic_script_with_tx_policies() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "bimbam_script",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "bimbam_script",
            wallet = "wallet"
        )
    );

    let a = 1000u64;
    let b = 2000u32;
    let result = script_instance.main(a, b).call().await?;
    assert_eq!(result.value, "hello");

    // ANCHOR: script_with_tx_policies
    let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
    let result = script_instance
        .main(a, b)
        .with_tx_policies(tx_policies)
        .call()
        .await?;
    // ANCHOR_END: script_with_tx_policies
    assert_eq!(result.value, "hello");

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "transfer_script",
            project = "e2e/sway/scripts/transfer_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "transfer_script",
            wallet = "wallet"
        )
    );

    let provider = wallet.try_provider()?.clone();
    let mut receiver = WalletUnlocked::new_random(None);
    receiver.set_provider(provider);

    let amount = 1000;
    let asset_id = AssetId::zeroed();
    let script_call = script_instance.main(
        amount,
        asset_id,
        Identity::Address(receiver.address().into()),
    );
    let inputs = wallet
        .get_asset_inputs_for_amount(asset_id, amount, None)
        .await?;
    let output = Output::change(wallet.address().into(), 0, asset_id);
    let _ = script_call
        .with_inputs(inputs)
        .with_outputs(vec![output])
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    let receiver_balance = receiver.get_asset_balance(&asset_id).await?;
    assert_eq!(receiver_balance, amount);

    Ok(())
}

#[tokio::test]
async fn test_script_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };
    let response = script_instance.main(my_struct).call().await?;

    assert_eq!(response.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_enum"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_enum = MyEnum::Two;
    let response = script_instance.main(my_enum).call().await?;

    assert_eq!(response.value, 2);
    Ok(())
}

#[tokio::test]
async fn test_script_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_array"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_array: [u64; 4] = [1, 2, 3, 4];
    let response = script_instance.main(my_array).call().await?;

    assert_eq!(response.value, 10);
    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_on_script_call() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_needs_custom_decoder"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    {
        // Will fail if max_tokens too low
        script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 101,
                ..Default::default()
            })
            .call()
            .await
            .expect_err(
                "Should fail because return type has more tokens than what is allowed by default",
            );
    }
    {
        // When the token limit is bumped should pass
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(response, [0u8; 1000]);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };

    // ANCHOR: submit_response_script
    let submitted_tx = script_instance.main(my_struct).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;
    // ANCHOR_END: submit_response_script

    assert_eq!(value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_transaction_builder() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.try_provider()?;

    // ANCHOR: script_call_tb
    let script_call_handler = script_instance.main(1, 2);

    let mut tb = script_call_handler.transaction_builder().await?;

    // customize the builder...

    wallet.adjust_for_fee(&mut tb, 0).await?;
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(provider).await?;

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let tx_status = provider.tx_status(&tx_id).await?;

    let response = script_call_handler.get_response_from(tx_status)?;

    assert_eq!(response.value, "hello");
    // ANCHOR_END: script_call_tb

    Ok(())
}

#[tokio::test]
async fn script_encoder_config_is_applied() {
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/basic_script/out/release/basic_script-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await.expect("");
    let bin_path = "sway/scripts/basic_script/out/release/basic_script.bin";

    let script_instance_without_encoder_config = MyScript::new(wallet.clone(), bin_path);
    {
        let _encoding_ok = script_instance_without_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let script_instance_with_encoder_config =
            MyScript::new(wallet.clone(), bin_path).with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));

        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));
    }
}
#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.provider().cloned();

    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let script_instance = script_instance.with_account(no_funds_wallet);

    let value = script_instance
        .main(1000, 2000)
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value.as_ref(), "hello");

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_builder() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    // ANCHOR: preload_low_level
    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular
        .convert_to_loader()?
        .with_configurables(configurables);

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;
    // ANCHOR_END: preload_low_level

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_high_level() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let mut my_script = my_script.with_configurables(configurables);

    let arg = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    };
    let secret = my_script
        .convert_into_loader()
        .await?
        .main(arg)
        .call()
        .await?
        .value;

    assert_eq!(secret, 10001);

    Ok(())
}

#[tokio::test]
async fn high_level_blob_upload_sets_max_fee_tolerance() -> Result<()> {
    let node_config = NodeConfig {
        starting_gas_price: 1000000000,
        ..Default::default()
    };
    let mut wallet = WalletUnlocked::new_random(None);
    let coins = setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, u64::MAX);
    let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let loader = Executable::from_bytes(std::fs::read(
        "sway/scripts/script_blobs/out/release/script_blobs.bin",
    )?)
    .convert_to_loader()?;

    let zero_tolerance_fee = {
        let mut tb = BlobTransactionBuilder::default()
            .with_blob(loader.blob())
            .with_max_fee_estimation_tolerance(0.);

        wallet.adjust_for_fee(&mut tb, 0).await?;

        wallet.add_witnesses(&mut tb)?;
        let tx = tb.build(&provider).await?;
        tx.max_fee().unwrap()
    };

    let mut my_script = my_script;
    my_script.convert_into_loader().await?;

    let max_fee_of_sent_blob_tx = provider
        .get_transactions(PaginationRequest {
            cursor: None,
            results: 100,
            direction: PageDirection::Forward,
        })
        .await?
        .results
        .into_iter()
        .find_map(|tx| {
            if let TransactionType::Blob(blob_transaction) = tx.transaction {
                blob_transaction.max_fee()
            } else {
                None
            }
        })
        .unwrap();

    assert_eq!(
        max_fee_of_sent_blob_tx,
        (zero_tolerance_fee as f32 * (1.0 + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)).ceil() as u64,
        "the blob upload tx should have had the max fee increased by the default estimation tolerance"
    );

    Ok(())
}

#[tokio::test]
async fn no_data_section_blob_run() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/empty",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let mut my_script = my_script;

    // ANCHOR: preload_high_level
    my_script.convert_into_loader().await?.main().call().await?;
    // ANCHOR_END: preload_high_level

    Ok(())
}

#[tokio::test]
async fn loader_script_calling_loader_proxy() -> Result<()> {
    setup_program_test!(
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            ),
            Contract(name = "MyProxy", project = "e2e/sway/contracts/proxy"),
            Script(name = "MyScript", project = "e2e/sway/scripts/script_proxy"),
        ),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id.clone(), wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let mut my_script = my_script;
    let result = my_script
        .convert_into_loader()
        .await?
        .main(proxy_id.clone())
        .with_contract_ids(&[contract_id, proxy_id])
        .call()
        .await?;

    assert!(result.value);

    Ok(())
}

#[tokio::test]
async fn loader_can_be_presented_as_a_normal_script_with_shifted_configurables() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular.clone().convert_to_loader()?;

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let configurables: Configurables = configurables.into();

    let shifted_configurables = configurables
        .with_shifted_offsets(-(regular.data_offset_in_code().unwrap() as i64))
        .unwrap()
        .with_shifted_offsets(loader.data_offset_in_code() as i64)
        .unwrap();

    let loader_posing_as_normal_script =
        Executable::from_bytes(loader.code()).with_configurables(shifted_configurables);

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader_posing_as_normal_script.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;

    Ok(())
}\n```

Furthermore, if you need to separate submission from value retrieval for any reason, you can do so as follows:

```rust\nuse std::time::Duration;

use fuel_tx::Output;
use fuels::{
    client::{PageDirection, PaginationRequest},
    core::{
        codec::{DecoderConfig, EncoderConfig},
        traits::Tokenizable,
        Configurables,
    },
    prelude::*,
    programs::{executable::Executable, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE},
    types::{Bits256, Identity},
};

#[tokio::test]
async fn main_function_arguments() -> Result<()> {
    // ANCHOR: script_with_arguments
    // The abigen is used for the same purpose as with contracts (Rust bindings)
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/scripts/arguments/out/release/arguments.bin";
    let script_instance = MyScript::new(wallet, bin_path);

    let bim = Bimbam { val: 90 };
    let bam = SugarySnack {
        twix: 100,
        mars: 1000,
    };

    let result = script_instance.main(bim, bam).call().await?;

    let expected = Bimbam { val: 2190 };
    assert_eq!(result.value, expected);
    // ANCHOR_END: script_with_arguments
    Ok(())
}

#[tokio::test]
async fn script_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let a = 4u64;
    let b = 2u32;
    let estimated_gas_used = script_instance
        .main(a, b)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = script_instance.main(a, b).call().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);

    Ok(())
}

#[tokio::test]
async fn test_basic_script_with_tx_policies() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "bimbam_script",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "bimbam_script",
            wallet = "wallet"
        )
    );

    let a = 1000u64;
    let b = 2000u32;
    let result = script_instance.main(a, b).call().await?;
    assert_eq!(result.value, "hello");

    // ANCHOR: script_with_tx_policies
    let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
    let result = script_instance
        .main(a, b)
        .with_tx_policies(tx_policies)
        .call()
        .await?;
    // ANCHOR_END: script_with_tx_policies
    assert_eq!(result.value, "hello");

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "transfer_script",
            project = "e2e/sway/scripts/transfer_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "transfer_script",
            wallet = "wallet"
        )
    );

    let provider = wallet.try_provider()?.clone();
    let mut receiver = WalletUnlocked::new_random(None);
    receiver.set_provider(provider);

    let amount = 1000;
    let asset_id = AssetId::zeroed();
    let script_call = script_instance.main(
        amount,
        asset_id,
        Identity::Address(receiver.address().into()),
    );
    let inputs = wallet
        .get_asset_inputs_for_amount(asset_id, amount, None)
        .await?;
    let output = Output::change(wallet.address().into(), 0, asset_id);
    let _ = script_call
        .with_inputs(inputs)
        .with_outputs(vec![output])
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    let receiver_balance = receiver.get_asset_balance(&asset_id).await?;
    assert_eq!(receiver_balance, amount);

    Ok(())
}

#[tokio::test]
async fn test_script_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };
    let response = script_instance.main(my_struct).call().await?;

    assert_eq!(response.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_enum"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_enum = MyEnum::Two;
    let response = script_instance.main(my_enum).call().await?;

    assert_eq!(response.value, 2);
    Ok(())
}

#[tokio::test]
async fn test_script_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_array"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_array: [u64; 4] = [1, 2, 3, 4];
    let response = script_instance.main(my_array).call().await?;

    assert_eq!(response.value, 10);
    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_on_script_call() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_needs_custom_decoder"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    {
        // Will fail if max_tokens too low
        script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 101,
                ..Default::default()
            })
            .call()
            .await
            .expect_err(
                "Should fail because return type has more tokens than what is allowed by default",
            );
    }
    {
        // When the token limit is bumped should pass
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(response, [0u8; 1000]);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };

    // ANCHOR: submit_response_script
    let submitted_tx = script_instance.main(my_struct).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;
    // ANCHOR_END: submit_response_script

    assert_eq!(value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_transaction_builder() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.try_provider()?;

    // ANCHOR: script_call_tb
    let script_call_handler = script_instance.main(1, 2);

    let mut tb = script_call_handler.transaction_builder().await?;

    // customize the builder...

    wallet.adjust_for_fee(&mut tb, 0).await?;
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(provider).await?;

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let tx_status = provider.tx_status(&tx_id).await?;

    let response = script_call_handler.get_response_from(tx_status)?;

    assert_eq!(response.value, "hello");
    // ANCHOR_END: script_call_tb

    Ok(())
}

#[tokio::test]
async fn script_encoder_config_is_applied() {
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/basic_script/out/release/basic_script-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await.expect("");
    let bin_path = "sway/scripts/basic_script/out/release/basic_script.bin";

    let script_instance_without_encoder_config = MyScript::new(wallet.clone(), bin_path);
    {
        let _encoding_ok = script_instance_without_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let script_instance_with_encoder_config =
            MyScript::new(wallet.clone(), bin_path).with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));

        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));
    }
}
#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.provider().cloned();

    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let script_instance = script_instance.with_account(no_funds_wallet);

    let value = script_instance
        .main(1000, 2000)
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value.as_ref(), "hello");

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_builder() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    // ANCHOR: preload_low_level
    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular
        .convert_to_loader()?
        .with_configurables(configurables);

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;
    // ANCHOR_END: preload_low_level

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_high_level() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let mut my_script = my_script.with_configurables(configurables);

    let arg = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    };
    let secret = my_script
        .convert_into_loader()
        .await?
        .main(arg)
        .call()
        .await?
        .value;

    assert_eq!(secret, 10001);

    Ok(())
}

#[tokio::test]
async fn high_level_blob_upload_sets_max_fee_tolerance() -> Result<()> {
    let node_config = NodeConfig {
        starting_gas_price: 1000000000,
        ..Default::default()
    };
    let mut wallet = WalletUnlocked::new_random(None);
    let coins = setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, u64::MAX);
    let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let loader = Executable::from_bytes(std::fs::read(
        "sway/scripts/script_blobs/out/release/script_blobs.bin",
    )?)
    .convert_to_loader()?;

    let zero_tolerance_fee = {
        let mut tb = BlobTransactionBuilder::default()
            .with_blob(loader.blob())
            .with_max_fee_estimation_tolerance(0.);

        wallet.adjust_for_fee(&mut tb, 0).await?;

        wallet.add_witnesses(&mut tb)?;
        let tx = tb.build(&provider).await?;
        tx.max_fee().unwrap()
    };

    let mut my_script = my_script;
    my_script.convert_into_loader().await?;

    let max_fee_of_sent_blob_tx = provider
        .get_transactions(PaginationRequest {
            cursor: None,
            results: 100,
            direction: PageDirection::Forward,
        })
        .await?
        .results
        .into_iter()
        .find_map(|tx| {
            if let TransactionType::Blob(blob_transaction) = tx.transaction {
                blob_transaction.max_fee()
            } else {
                None
            }
        })
        .unwrap();

    assert_eq!(
        max_fee_of_sent_blob_tx,
        (zero_tolerance_fee as f32 * (1.0 + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)).ceil() as u64,
        "the blob upload tx should have had the max fee increased by the default estimation tolerance"
    );

    Ok(())
}

#[tokio::test]
async fn no_data_section_blob_run() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/empty",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let mut my_script = my_script;

    // ANCHOR: preload_high_level
    my_script.convert_into_loader().await?.main().call().await?;
    // ANCHOR_END: preload_high_level

    Ok(())
}

#[tokio::test]
async fn loader_script_calling_loader_proxy() -> Result<()> {
    setup_program_test!(
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            ),
            Contract(name = "MyProxy", project = "e2e/sway/contracts/proxy"),
            Script(name = "MyScript", project = "e2e/sway/scripts/script_proxy"),
        ),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id.clone(), wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let mut my_script = my_script;
    let result = my_script
        .convert_into_loader()
        .await?
        .main(proxy_id.clone())
        .with_contract_ids(&[contract_id, proxy_id])
        .call()
        .await?;

    assert!(result.value);

    Ok(())
}

#[tokio::test]
async fn loader_can_be_presented_as_a_normal_script_with_shifted_configurables() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular.clone().convert_to_loader()?;

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let configurables: Configurables = configurables.into();

    let shifted_configurables = configurables
        .with_shifted_offsets(-(regular.data_offset_in_code().unwrap() as i64))
        .unwrap()
        .with_shifted_offsets(loader.data_offset_in_code() as i64)
        .unwrap();

    let loader_posing_as_normal_script =
        Executable::from_bytes(loader.code()).with_configurables(shifted_configurables);

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader_posing_as_normal_script.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;

    Ok(())
}\n```

Running scripts with transaction policies

The method for passing transaction policies is the same as with contracts. As a reminder, the workflow would look like this:

```rust\nuse std::time::Duration;

use fuel_tx::Output;
use fuels::{
    client::{PageDirection, PaginationRequest},
    core::{
        codec::{DecoderConfig, EncoderConfig},
        traits::Tokenizable,
        Configurables,
    },
    prelude::*,
    programs::{executable::Executable, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE},
    types::{Bits256, Identity},
};

#[tokio::test]
async fn main_function_arguments() -> Result<()> {
    // ANCHOR: script_with_arguments
    // The abigen is used for the same purpose as with contracts (Rust bindings)
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/scripts/arguments/out/release/arguments.bin";
    let script_instance = MyScript::new(wallet, bin_path);

    let bim = Bimbam { val: 90 };
    let bam = SugarySnack {
        twix: 100,
        mars: 1000,
    };

    let result = script_instance.main(bim, bam).call().await?;

    let expected = Bimbam { val: 2190 };
    assert_eq!(result.value, expected);
    // ANCHOR_END: script_with_arguments
    Ok(())
}

#[tokio::test]
async fn script_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let a = 4u64;
    let b = 2u32;
    let estimated_gas_used = script_instance
        .main(a, b)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = script_instance.main(a, b).call().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);

    Ok(())
}

#[tokio::test]
async fn test_basic_script_with_tx_policies() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "bimbam_script",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "bimbam_script",
            wallet = "wallet"
        )
    );

    let a = 1000u64;
    let b = 2000u32;
    let result = script_instance.main(a, b).call().await?;
    assert_eq!(result.value, "hello");

    // ANCHOR: script_with_tx_policies
    let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
    let result = script_instance
        .main(a, b)
        .with_tx_policies(tx_policies)
        .call()
        .await?;
    // ANCHOR_END: script_with_tx_policies
    assert_eq!(result.value, "hello");

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "transfer_script",
            project = "e2e/sway/scripts/transfer_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "transfer_script",
            wallet = "wallet"
        )
    );

    let provider = wallet.try_provider()?.clone();
    let mut receiver = WalletUnlocked::new_random(None);
    receiver.set_provider(provider);

    let amount = 1000;
    let asset_id = AssetId::zeroed();
    let script_call = script_instance.main(
        amount,
        asset_id,
        Identity::Address(receiver.address().into()),
    );
    let inputs = wallet
        .get_asset_inputs_for_amount(asset_id, amount, None)
        .await?;
    let output = Output::change(wallet.address().into(), 0, asset_id);
    let _ = script_call
        .with_inputs(inputs)
        .with_outputs(vec![output])
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    let receiver_balance = receiver.get_asset_balance(&asset_id).await?;
    assert_eq!(receiver_balance, amount);

    Ok(())
}

#[tokio::test]
async fn test_script_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };
    let response = script_instance.main(my_struct).call().await?;

    assert_eq!(response.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_enum"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_enum = MyEnum::Two;
    let response = script_instance.main(my_enum).call().await?;

    assert_eq!(response.value, 2);
    Ok(())
}

#[tokio::test]
async fn test_script_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_array"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_array: [u64; 4] = [1, 2, 3, 4];
    let response = script_instance.main(my_array).call().await?;

    assert_eq!(response.value, 10);
    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_on_script_call() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_needs_custom_decoder"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    {
        // Will fail if max_tokens too low
        script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 101,
                ..Default::default()
            })
            .call()
            .await
            .expect_err(
                "Should fail because return type has more tokens than what is allowed by default",
            );
    }
    {
        // When the token limit is bumped should pass
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(response, [0u8; 1000]);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };

    // ANCHOR: submit_response_script
    let submitted_tx = script_instance.main(my_struct).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;
    // ANCHOR_END: submit_response_script

    assert_eq!(value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_transaction_builder() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.try_provider()?;

    // ANCHOR: script_call_tb
    let script_call_handler = script_instance.main(1, 2);

    let mut tb = script_call_handler.transaction_builder().await?;

    // customize the builder...

    wallet.adjust_for_fee(&mut tb, 0).await?;
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(provider).await?;

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let tx_status = provider.tx_status(&tx_id).await?;

    let response = script_call_handler.get_response_from(tx_status)?;

    assert_eq!(response.value, "hello");
    // ANCHOR_END: script_call_tb

    Ok(())
}

#[tokio::test]
async fn script_encoder_config_is_applied() {
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/basic_script/out/release/basic_script-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await.expect("");
    let bin_path = "sway/scripts/basic_script/out/release/basic_script.bin";

    let script_instance_without_encoder_config = MyScript::new(wallet.clone(), bin_path);
    {
        let _encoding_ok = script_instance_without_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let script_instance_with_encoder_config =
            MyScript::new(wallet.clone(), bin_path).with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));

        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));
    }
}
#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.provider().cloned();

    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let script_instance = script_instance.with_account(no_funds_wallet);

    let value = script_instance
        .main(1000, 2000)
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value.as_ref(), "hello");

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_builder() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    // ANCHOR: preload_low_level
    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular
        .convert_to_loader()?
        .with_configurables(configurables);

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;
    // ANCHOR_END: preload_low_level

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_high_level() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let mut my_script = my_script.with_configurables(configurables);

    let arg = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    };
    let secret = my_script
        .convert_into_loader()
        .await?
        .main(arg)
        .call()
        .await?
        .value;

    assert_eq!(secret, 10001);

    Ok(())
}

#[tokio::test]
async fn high_level_blob_upload_sets_max_fee_tolerance() -> Result<()> {
    let node_config = NodeConfig {
        starting_gas_price: 1000000000,
        ..Default::default()
    };
    let mut wallet = WalletUnlocked::new_random(None);
    let coins = setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, u64::MAX);
    let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let loader = Executable::from_bytes(std::fs::read(
        "sway/scripts/script_blobs/out/release/script_blobs.bin",
    )?)
    .convert_to_loader()?;

    let zero_tolerance_fee = {
        let mut tb = BlobTransactionBuilder::default()
            .with_blob(loader.blob())
            .with_max_fee_estimation_tolerance(0.);

        wallet.adjust_for_fee(&mut tb, 0).await?;

        wallet.add_witnesses(&mut tb)?;
        let tx = tb.build(&provider).await?;
        tx.max_fee().unwrap()
    };

    let mut my_script = my_script;
    my_script.convert_into_loader().await?;

    let max_fee_of_sent_blob_tx = provider
        .get_transactions(PaginationRequest {
            cursor: None,
            results: 100,
            direction: PageDirection::Forward,
        })
        .await?
        .results
        .into_iter()
        .find_map(|tx| {
            if let TransactionType::Blob(blob_transaction) = tx.transaction {
                blob_transaction.max_fee()
            } else {
                None
            }
        })
        .unwrap();

    assert_eq!(
        max_fee_of_sent_blob_tx,
        (zero_tolerance_fee as f32 * (1.0 + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)).ceil() as u64,
        "the blob upload tx should have had the max fee increased by the default estimation tolerance"
    );

    Ok(())
}

#[tokio::test]
async fn no_data_section_blob_run() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/empty",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let mut my_script = my_script;

    // ANCHOR: preload_high_level
    my_script.convert_into_loader().await?.main().call().await?;
    // ANCHOR_END: preload_high_level

    Ok(())
}

#[tokio::test]
async fn loader_script_calling_loader_proxy() -> Result<()> {
    setup_program_test!(
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            ),
            Contract(name = "MyProxy", project = "e2e/sway/contracts/proxy"),
            Script(name = "MyScript", project = "e2e/sway/scripts/script_proxy"),
        ),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id.clone(), wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let mut my_script = my_script;
    let result = my_script
        .convert_into_loader()
        .await?
        .main(proxy_id.clone())
        .with_contract_ids(&[contract_id, proxy_id])
        .call()
        .await?;

    assert!(result.value);

    Ok(())
}

#[tokio::test]
async fn loader_can_be_presented_as_a_normal_script_with_shifted_configurables() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular.clone().convert_to_loader()?;

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let configurables: Configurables = configurables.into();

    let shifted_configurables = configurables
        .with_shifted_offsets(-(regular.data_offset_in_code().unwrap() as i64))
        .unwrap()
        .with_shifted_offsets(loader.data_offset_in_code() as i64)
        .unwrap();

    let loader_posing_as_normal_script =
        Executable::from_bytes(loader.code()).with_configurables(shifted_configurables);

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader_posing_as_normal_script.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;

    Ok(())
}\n```

Logs

Script calls provide the same logging functions, decode_logs() and decode_logs_with_type<T>(), as contract calls. As a reminder, the workflow looks like this:

```rust\nuse fuels::{
    core::codec::DecoderConfig,
    prelude::*,
    types::{errors::transaction::Reason, AsciiString, Bits256, SizedAsciiString},
};

#[tokio::test]
async fn test_parse_logged_variables() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: produce_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_variables().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_bits256 = response.decode_logs_with_type::<Bits256>()?;
    let log_string = response.decode_logs_with_type::<SizedAsciiString<4>>()?;
    let log_array = response.decode_logs_with_type::<[u8; 3]>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_bits256, vec![expected_bits256]);
    assert_eq!(log_string, vec!["Fuel"]);
    assert_eq!(log_array, vec![[1, 2, 3]]);
    // ANCHOR_END: produce_logs

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_values() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_values().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_custom_types().call().await?;

    let log_test_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_test_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_tuple = response.decode_logs_with_type::<(TestStruct, TestEnum)>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;

    assert_eq!(log_test_struct, vec![expected_struct.clone()]);
    assert_eq!(log_test_enum, vec![expected_enum.clone()]);
    assert_eq!(log_tuple, vec![(expected_struct, expected_enum)]);

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_generic_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_generic_types().call().await?;

    let log_struct = response.decode_logs_with_type::<StructWithGeneric<[_; 3]>>()?;
    let log_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_struct_nested =
        response.decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<[_; 3]>>>()?;
    let log_struct_deeply_nested = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<[_; 3]>>,
    >>()?;

    let l = [1u8, 2u8, 3u8];
    let expected_struct = StructWithGeneric {
        field_1: l,
        field_2: 64,
    };
    let expected_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_struct_nested, vec![expected_nested_struct]);
    assert_eq!(
        log_struct_deeply_nested,
        vec![expected_deeply_nested_struct]
    );

    Ok(())
}

#[tokio::test]
async fn test_decode_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: decode_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_multiple_logs().call().await?;
    let logs = response.decode_logs();
    // ANCHOR_END: decode_logs

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_generic_struct:?}"),
    ];

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
async fn test_decode_logs_with_no_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let logs = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .decode_logs();

    assert!(logs.filter_succeeded().is_empty());

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.produce_logs_values();
    let call_handler_2 = contract_methods.produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_multiple_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_handler_1 = contract_instance.methods().produce_logs_values();
    let call_handler_2 = contract_instance2.methods().produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs"),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id.clone())
        .with_contracts(&[&contract_instance]);

    let call_handler_2 = contract_caller_instance2
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

fn assert_revert_containing_msg(msg: &str, error: Error) {
    assert!(matches!(error, Error::Transaction(Reason::Reverted { .. })));
    if let Error::Transaction(Reason::Reverted { reason, .. }) = error {
        assert!(
            reason.contains(msg),
            "message: \"{msg}\" not contained in reason: \"{reason}\""
        );
    }
}

#[tokio::test]
async fn test_revert_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        ($method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .call()
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
        ($method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(require_primitive, call, "42");
        reverts_with_msg!(require_primitive, simulate, "42");

        reverts_with_msg!(require_string, call, "fuel");
        reverts_with_msg!(require_string, simulate, "fuel");

        reverts_with_msg!(require_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            require_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(require_with_additional_logs, call, "64");
        reverts_with_msg!(require_with_additional_logs, simulate, "64");
    }
    {
        reverts_with_msg!(rev_w_log_primitive, call, "42");
        reverts_with_msg!(rev_w_log_primitive, simulate, "42");

        reverts_with_msg!(rev_w_log_string, call, "fuel");
        reverts_with_msg!(rev_w_log_string, simulate, "fuel");

        reverts_with_msg!(rev_w_log_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            rev_w_log_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_multi_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let contract_methods2 = contract_instance2.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods2.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods2.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_decode_logs() -> Result<()> {
    // ANCHOR: script_logs
    abigen!(Script(
        name = "LogScript",
        abi = "e2e/sway/logs/script_logs/out/release/script_logs-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/logs/script_logs/out/release/script_logs.bin";
    let instance = LogScript::new(wallet.clone(), bin_path);

    let response = instance.main().call().await?;

    let logs = response.decode_logs();
    let log_u64 = response.decode_logs_with_type::<u64>()?;
    // ANCHOR_END: script_logs

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_tuple = (expected_struct.clone(), expected_enum.clone());
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 128u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_tuple:?}"),
        format!("{expected_generic_struct:?}"),
        format!("{expected_generic_enum:?}"),
        format!("{expected_nested_struct:?}"),
        format!("{expected_deeply_nested_struct:?}"),
    ];

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?
        .decode_logs();

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_logs_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Script(
                name = "LogScript",
                project = "e2e/sway/logs/script_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let expected_num_contract_logs = 4;

    let expected_script_logs: Vec<String> = vec![
        // Contract logs
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        // Script logs
        format!("{:?}", true),
        format!("{:?}", 42),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    // ANCHOR: instance_to_contract_id
    let contract_id: ContractId = contract_instance.id().into();
    // ANCHOR_END: instance_to_contract_id

    // ANCHOR: external_contract_ids
    let response = script_instance
        .main(contract_id)
        .with_contract_ids(&[contract_id.into()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    // ANCHOR: external_contract
    let response = script_instance
        .main(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    {
        let num_contract_logs = response
            .receipts
            .iter()
            .filter(|receipt| matches!(receipt, Receipt::LogData { id, .. } | Receipt::Log { id, .. } if *id == contract_id))
            .count();

        assert_eq!(num_contract_logs, expected_num_contract_logs);
    }
    {
        let logs = response.decode_logs();

        assert_eq!(logs.filter_succeeded(), expected_script_logs);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_decode_logs_with_type() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let response = script_instance.main().call().await?;

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    let log_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_generic_struct = response.decode_logs_with_type::<StructWithGeneric<TestStruct>>()?;
    let log_generic_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_nested_struct = response
        .decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<TestStruct>>>()?;
    let log_deeply_nested_struct = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<TestStruct>>,
    >>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![128, 64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_generic_struct, vec![expected_generic_struct]);
    assert_eq!(log_generic_enum, vec![expected_generic_enum]);
    assert_eq!(log_nested_struct, vec![expected_nested_struct]);
    assert_eq!(
        log_deeply_nested_struct,
        vec![expected_deeply_nested_struct]
    );
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_script_require_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_revert_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(MatchEnum::RequirePrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RequirePrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RequireString, call, "fuel");
        reverts_with_msg!(MatchEnum::RequireString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, call, "64");
        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, simulate, "64");
    }
    {
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RevWLogString, call, "fuel");
        reverts_with_msg!(MatchEnum::RevWLogString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let error = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractLogs",
                project = "e2e/sway/logs/contract_logs",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "ContractLogs",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let lib_contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_instance.methods().produce_logs_values();

    let call_handler_2 = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&lib_contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let error = multi_call_handler
        .call::<((), ())>()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_script_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_loader_script_require_from_loader_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let contract_binary = "sway/contracts/lib_contract/out/release/lib_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;
    let contract_instance = MyContract::new(contract_id, wallet);

    let mut script_instance = script_instance;
    script_instance.convert_into_loader().await?;

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

fn assert_assert_eq_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left == right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

fn assert_assert_ne_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left != right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

#[tokio::test]
async fn test_contract_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/contracts/asserts"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        (($($arg: expr,)*), $method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        (($($arg: expr,)*), $method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!((32, 64,), assert_primitive, call, "assertion failed");
        reverts_with_msg!((32, 64,), assert_primitive, simulate, "assertion failed");
    }

    macro_rules! reverts_with_assert_eq_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_eq_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, call, "assertion failed");
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;
        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            simulate,
            "assertion failed"
        );
    }

    macro_rules! reverts_with_assert_ne_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_ne_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, call, "assertion failed");
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            simulate,
            "assertion failed"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_script_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_asserts"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    macro_rules! reverts_with_assert_eq_ne_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            call,
            "assertion failed"
        );
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            simulate,
            "assertion failed"
        );
    }
    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }

    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }

    Ok(())
}

#[tokio::test]
async fn contract_token_ops_error_messages() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/token_ops"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let contract_id = contract_instance.contract_id();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());
        let address = wallet.address();

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .simulate(Execution::Realistic)
            .await
            .expect_err("should return a revert error");
        assert_revert_containing_msg("failed transfer to address", error);

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .call()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("failed transfer to address", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_log_results() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let response = contract_instance
        .methods()
        .produce_bad_logs()
        .call()
        .await?;

    let log = response.decode_logs();

    let expected_err = format!(
        "codec: missing log formatter for log_id: `LogId({:?}, \"128\")`, data: `{:?}`. \
         Consider adding external contracts using `with_contracts()`",
        contract_instance.id().hash,
        [0u8; 8]
    );

    let succeeded = log.filter_succeeded();
    let failed = log.filter_failed();
    assert_eq!(succeeded, vec!["123".to_string()]);
    assert_eq!(failed.first().unwrap().to_string(), expected_err);

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_contract_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: decoding with too low max_tokens fails
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "Should have failed since there are more tokens than what is supported by default.",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "Should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Single call: increasing limits makes the test pass
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }
    {
        // Multi call: decoding with too low max_tokens will fail
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "should have failed since there are more tokens than what is supported by default",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Multi call: increasing limits makes the test pass
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_script_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_needs_custom_decoder_logging"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    {
        // Cannot decode the produced log with too low max_tokens
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response
            .decode_logs_with_type::<[u8; 1000]>()
            .expect_err("Cannot decode the log with default decoder config");

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty())
    }
    {
        // When the token limit is bumped log decoding succeeds
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty())
    }

    Ok(())
}

#[tokio::test]
async fn contract_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.produce_string_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let response = contract_methods.produce_string_log().call().await?;
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let response = contract_methods.produce_bytes_log().call().await?;
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let response = contract_methods.produce_raw_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let response = contract_methods.produce_vec_log().call().await?;
        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}

#[tokio::test]
async fn script_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_heap_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    let response = script_instance.main().call().await?;

    {
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}\n```

Calling contracts from scripts

Scripts use the same interfaces for setting external contracts as contract methods.

Below is an example that uses with_contracts(&[&contract_instance, ...]).

```rust\nuse fuels::{
    core::codec::DecoderConfig,
    prelude::*,
    types::{errors::transaction::Reason, AsciiString, Bits256, SizedAsciiString},
};

#[tokio::test]
async fn test_parse_logged_variables() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: produce_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_variables().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_bits256 = response.decode_logs_with_type::<Bits256>()?;
    let log_string = response.decode_logs_with_type::<SizedAsciiString<4>>()?;
    let log_array = response.decode_logs_with_type::<[u8; 3]>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_bits256, vec![expected_bits256]);
    assert_eq!(log_string, vec!["Fuel"]);
    assert_eq!(log_array, vec![[1, 2, 3]]);
    // ANCHOR_END: produce_logs

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_values() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_values().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_custom_types().call().await?;

    let log_test_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_test_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_tuple = response.decode_logs_with_type::<(TestStruct, TestEnum)>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;

    assert_eq!(log_test_struct, vec![expected_struct.clone()]);
    assert_eq!(log_test_enum, vec![expected_enum.clone()]);
    assert_eq!(log_tuple, vec![(expected_struct, expected_enum)]);

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_generic_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_generic_types().call().await?;

    let log_struct = response.decode_logs_with_type::<StructWithGeneric<[_; 3]>>()?;
    let log_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_struct_nested =
        response.decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<[_; 3]>>>()?;
    let log_struct_deeply_nested = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<[_; 3]>>,
    >>()?;

    let l = [1u8, 2u8, 3u8];
    let expected_struct = StructWithGeneric {
        field_1: l,
        field_2: 64,
    };
    let expected_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_struct_nested, vec![expected_nested_struct]);
    assert_eq!(
        log_struct_deeply_nested,
        vec![expected_deeply_nested_struct]
    );

    Ok(())
}

#[tokio::test]
async fn test_decode_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: decode_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_multiple_logs().call().await?;
    let logs = response.decode_logs();
    // ANCHOR_END: decode_logs

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_generic_struct:?}"),
    ];

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
async fn test_decode_logs_with_no_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let logs = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .decode_logs();

    assert!(logs.filter_succeeded().is_empty());

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.produce_logs_values();
    let call_handler_2 = contract_methods.produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_multiple_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_handler_1 = contract_instance.methods().produce_logs_values();
    let call_handler_2 = contract_instance2.methods().produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs"),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id.clone())
        .with_contracts(&[&contract_instance]);

    let call_handler_2 = contract_caller_instance2
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

fn assert_revert_containing_msg(msg: &str, error: Error) {
    assert!(matches!(error, Error::Transaction(Reason::Reverted { .. })));
    if let Error::Transaction(Reason::Reverted { reason, .. }) = error {
        assert!(
            reason.contains(msg),
            "message: \"{msg}\" not contained in reason: \"{reason}\""
        );
    }
}

#[tokio::test]
async fn test_revert_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        ($method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .call()
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
        ($method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(require_primitive, call, "42");
        reverts_with_msg!(require_primitive, simulate, "42");

        reverts_with_msg!(require_string, call, "fuel");
        reverts_with_msg!(require_string, simulate, "fuel");

        reverts_with_msg!(require_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            require_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(require_with_additional_logs, call, "64");
        reverts_with_msg!(require_with_additional_logs, simulate, "64");
    }
    {
        reverts_with_msg!(rev_w_log_primitive, call, "42");
        reverts_with_msg!(rev_w_log_primitive, simulate, "42");

        reverts_with_msg!(rev_w_log_string, call, "fuel");
        reverts_with_msg!(rev_w_log_string, simulate, "fuel");

        reverts_with_msg!(rev_w_log_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            rev_w_log_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_multi_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let contract_methods2 = contract_instance2.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods2.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods2.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_decode_logs() -> Result<()> {
    // ANCHOR: script_logs
    abigen!(Script(
        name = "LogScript",
        abi = "e2e/sway/logs/script_logs/out/release/script_logs-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/logs/script_logs/out/release/script_logs.bin";
    let instance = LogScript::new(wallet.clone(), bin_path);

    let response = instance.main().call().await?;

    let logs = response.decode_logs();
    let log_u64 = response.decode_logs_with_type::<u64>()?;
    // ANCHOR_END: script_logs

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_tuple = (expected_struct.clone(), expected_enum.clone());
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 128u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_tuple:?}"),
        format!("{expected_generic_struct:?}"),
        format!("{expected_generic_enum:?}"),
        format!("{expected_nested_struct:?}"),
        format!("{expected_deeply_nested_struct:?}"),
    ];

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?
        .decode_logs();

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_logs_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Script(
                name = "LogScript",
                project = "e2e/sway/logs/script_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let expected_num_contract_logs = 4;

    let expected_script_logs: Vec<String> = vec![
        // Contract logs
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        // Script logs
        format!("{:?}", true),
        format!("{:?}", 42),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    // ANCHOR: instance_to_contract_id
    let contract_id: ContractId = contract_instance.id().into();
    // ANCHOR_END: instance_to_contract_id

    // ANCHOR: external_contract_ids
    let response = script_instance
        .main(contract_id)
        .with_contract_ids(&[contract_id.into()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    // ANCHOR: external_contract
    let response = script_instance
        .main(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    {
        let num_contract_logs = response
            .receipts
            .iter()
            .filter(|receipt| matches!(receipt, Receipt::LogData { id, .. } | Receipt::Log { id, .. } if *id == contract_id))
            .count();

        assert_eq!(num_contract_logs, expected_num_contract_logs);
    }
    {
        let logs = response.decode_logs();

        assert_eq!(logs.filter_succeeded(), expected_script_logs);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_decode_logs_with_type() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let response = script_instance.main().call().await?;

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    let log_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_generic_struct = response.decode_logs_with_type::<StructWithGeneric<TestStruct>>()?;
    let log_generic_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_nested_struct = response
        .decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<TestStruct>>>()?;
    let log_deeply_nested_struct = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<TestStruct>>,
    >>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![128, 64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_generic_struct, vec![expected_generic_struct]);
    assert_eq!(log_generic_enum, vec![expected_generic_enum]);
    assert_eq!(log_nested_struct, vec![expected_nested_struct]);
    assert_eq!(
        log_deeply_nested_struct,
        vec![expected_deeply_nested_struct]
    );
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_script_require_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_revert_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(MatchEnum::RequirePrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RequirePrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RequireString, call, "fuel");
        reverts_with_msg!(MatchEnum::RequireString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, call, "64");
        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, simulate, "64");
    }
    {
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RevWLogString, call, "fuel");
        reverts_with_msg!(MatchEnum::RevWLogString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let error = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractLogs",
                project = "e2e/sway/logs/contract_logs",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "ContractLogs",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let lib_contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_instance.methods().produce_logs_values();

    let call_handler_2 = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&lib_contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let error = multi_call_handler
        .call::<((), ())>()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_script_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_loader_script_require_from_loader_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let contract_binary = "sway/contracts/lib_contract/out/release/lib_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;
    let contract_instance = MyContract::new(contract_id, wallet);

    let mut script_instance = script_instance;
    script_instance.convert_into_loader().await?;

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

fn assert_assert_eq_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left == right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

fn assert_assert_ne_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left != right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

#[tokio::test]
async fn test_contract_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/contracts/asserts"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        (($($arg: expr,)*), $method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        (($($arg: expr,)*), $method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!((32, 64,), assert_primitive, call, "assertion failed");
        reverts_with_msg!((32, 64,), assert_primitive, simulate, "assertion failed");
    }

    macro_rules! reverts_with_assert_eq_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_eq_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, call, "assertion failed");
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;
        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            simulate,
            "assertion failed"
        );
    }

    macro_rules! reverts_with_assert_ne_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_ne_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, call, "assertion failed");
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            simulate,
            "assertion failed"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_script_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_asserts"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    macro_rules! reverts_with_assert_eq_ne_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            call,
            "assertion failed"
        );
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            simulate,
            "assertion failed"
        );
    }
    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }

    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }

    Ok(())
}

#[tokio::test]
async fn contract_token_ops_error_messages() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/token_ops"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let contract_id = contract_instance.contract_id();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());
        let address = wallet.address();

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .simulate(Execution::Realistic)
            .await
            .expect_err("should return a revert error");
        assert_revert_containing_msg("failed transfer to address", error);

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .call()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("failed transfer to address", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_log_results() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let response = contract_instance
        .methods()
        .produce_bad_logs()
        .call()
        .await?;

    let log = response.decode_logs();

    let expected_err = format!(
        "codec: missing log formatter for log_id: `LogId({:?}, \"128\")`, data: `{:?}`. \
         Consider adding external contracts using `with_contracts()`",
        contract_instance.id().hash,
        [0u8; 8]
    );

    let succeeded = log.filter_succeeded();
    let failed = log.filter_failed();
    assert_eq!(succeeded, vec!["123".to_string()]);
    assert_eq!(failed.first().unwrap().to_string(), expected_err);

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_contract_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: decoding with too low max_tokens fails
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "Should have failed since there are more tokens than what is supported by default.",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "Should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Single call: increasing limits makes the test pass
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }
    {
        // Multi call: decoding with too low max_tokens will fail
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "should have failed since there are more tokens than what is supported by default",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Multi call: increasing limits makes the test pass
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_script_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_needs_custom_decoder_logging"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    {
        // Cannot decode the produced log with too low max_tokens
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response
            .decode_logs_with_type::<[u8; 1000]>()
            .expect_err("Cannot decode the log with default decoder config");

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty())
    }
    {
        // When the token limit is bumped log decoding succeeds
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty())
    }

    Ok(())
}

#[tokio::test]
async fn contract_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.produce_string_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let response = contract_methods.produce_string_log().call().await?;
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let response = contract_methods.produce_bytes_log().call().await?;
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let response = contract_methods.produce_raw_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let response = contract_methods.produce_vec_log().call().await?;
        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}

#[tokio::test]
async fn script_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_heap_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    let response = script_instance.main().call().await?;

    {
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}\n```

And this is an example that uses with_contract_ids(&[&contract_id, ...]).

```rust\nuse fuels::{
    core::codec::DecoderConfig,
    prelude::*,
    types::{errors::transaction::Reason, AsciiString, Bits256, SizedAsciiString},
};

#[tokio::test]
async fn test_parse_logged_variables() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: produce_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_variables().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_bits256 = response.decode_logs_with_type::<Bits256>()?;
    let log_string = response.decode_logs_with_type::<SizedAsciiString<4>>()?;
    let log_array = response.decode_logs_with_type::<[u8; 3]>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_bits256, vec![expected_bits256]);
    assert_eq!(log_string, vec!["Fuel"]);
    assert_eq!(log_array, vec![[1, 2, 3]]);
    // ANCHOR_END: produce_logs

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_values() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_values().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_custom_types().call().await?;

    let log_test_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_test_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_tuple = response.decode_logs_with_type::<(TestStruct, TestEnum)>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;

    assert_eq!(log_test_struct, vec![expected_struct.clone()]);
    assert_eq!(log_test_enum, vec![expected_enum.clone()]);
    assert_eq!(log_tuple, vec![(expected_struct, expected_enum)]);

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_generic_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_generic_types().call().await?;

    let log_struct = response.decode_logs_with_type::<StructWithGeneric<[_; 3]>>()?;
    let log_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_struct_nested =
        response.decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<[_; 3]>>>()?;
    let log_struct_deeply_nested = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<[_; 3]>>,
    >>()?;

    let l = [1u8, 2u8, 3u8];
    let expected_struct = StructWithGeneric {
        field_1: l,
        field_2: 64,
    };
    let expected_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_struct_nested, vec![expected_nested_struct]);
    assert_eq!(
        log_struct_deeply_nested,
        vec![expected_deeply_nested_struct]
    );

    Ok(())
}

#[tokio::test]
async fn test_decode_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: decode_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_multiple_logs().call().await?;
    let logs = response.decode_logs();
    // ANCHOR_END: decode_logs

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_generic_struct:?}"),
    ];

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
async fn test_decode_logs_with_no_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let logs = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .decode_logs();

    assert!(logs.filter_succeeded().is_empty());

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.produce_logs_values();
    let call_handler_2 = contract_methods.produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_multiple_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_handler_1 = contract_instance.methods().produce_logs_values();
    let call_handler_2 = contract_instance2.methods().produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs"),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id.clone())
        .with_contracts(&[&contract_instance]);

    let call_handler_2 = contract_caller_instance2
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

fn assert_revert_containing_msg(msg: &str, error: Error) {
    assert!(matches!(error, Error::Transaction(Reason::Reverted { .. })));
    if let Error::Transaction(Reason::Reverted { reason, .. }) = error {
        assert!(
            reason.contains(msg),
            "message: \"{msg}\" not contained in reason: \"{reason}\""
        );
    }
}

#[tokio::test]
async fn test_revert_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        ($method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .call()
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
        ($method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(require_primitive, call, "42");
        reverts_with_msg!(require_primitive, simulate, "42");

        reverts_with_msg!(require_string, call, "fuel");
        reverts_with_msg!(require_string, simulate, "fuel");

        reverts_with_msg!(require_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            require_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(require_with_additional_logs, call, "64");
        reverts_with_msg!(require_with_additional_logs, simulate, "64");
    }
    {
        reverts_with_msg!(rev_w_log_primitive, call, "42");
        reverts_with_msg!(rev_w_log_primitive, simulate, "42");

        reverts_with_msg!(rev_w_log_string, call, "fuel");
        reverts_with_msg!(rev_w_log_string, simulate, "fuel");

        reverts_with_msg!(rev_w_log_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            rev_w_log_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_multi_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let contract_methods2 = contract_instance2.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods2.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods2.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_decode_logs() -> Result<()> {
    // ANCHOR: script_logs
    abigen!(Script(
        name = "LogScript",
        abi = "e2e/sway/logs/script_logs/out/release/script_logs-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/logs/script_logs/out/release/script_logs.bin";
    let instance = LogScript::new(wallet.clone(), bin_path);

    let response = instance.main().call().await?;

    let logs = response.decode_logs();
    let log_u64 = response.decode_logs_with_type::<u64>()?;
    // ANCHOR_END: script_logs

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_tuple = (expected_struct.clone(), expected_enum.clone());
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 128u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_tuple:?}"),
        format!("{expected_generic_struct:?}"),
        format!("{expected_generic_enum:?}"),
        format!("{expected_nested_struct:?}"),
        format!("{expected_deeply_nested_struct:?}"),
    ];

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?
        .decode_logs();

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_logs_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Script(
                name = "LogScript",
                project = "e2e/sway/logs/script_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let expected_num_contract_logs = 4;

    let expected_script_logs: Vec<String> = vec![
        // Contract logs
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        // Script logs
        format!("{:?}", true),
        format!("{:?}", 42),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    // ANCHOR: instance_to_contract_id
    let contract_id: ContractId = contract_instance.id().into();
    // ANCHOR_END: instance_to_contract_id

    // ANCHOR: external_contract_ids
    let response = script_instance
        .main(contract_id)
        .with_contract_ids(&[contract_id.into()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    // ANCHOR: external_contract
    let response = script_instance
        .main(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    {
        let num_contract_logs = response
            .receipts
            .iter()
            .filter(|receipt| matches!(receipt, Receipt::LogData { id, .. } | Receipt::Log { id, .. } if *id == contract_id))
            .count();

        assert_eq!(num_contract_logs, expected_num_contract_logs);
    }
    {
        let logs = response.decode_logs();

        assert_eq!(logs.filter_succeeded(), expected_script_logs);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_decode_logs_with_type() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let response = script_instance.main().call().await?;

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    let log_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_generic_struct = response.decode_logs_with_type::<StructWithGeneric<TestStruct>>()?;
    let log_generic_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_nested_struct = response
        .decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<TestStruct>>>()?;
    let log_deeply_nested_struct = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<TestStruct>>,
    >>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![128, 64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_generic_struct, vec![expected_generic_struct]);
    assert_eq!(log_generic_enum, vec![expected_generic_enum]);
    assert_eq!(log_nested_struct, vec![expected_nested_struct]);
    assert_eq!(
        log_deeply_nested_struct,
        vec![expected_deeply_nested_struct]
    );
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_script_require_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_revert_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(MatchEnum::RequirePrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RequirePrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RequireString, call, "fuel");
        reverts_with_msg!(MatchEnum::RequireString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, call, "64");
        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, simulate, "64");
    }
    {
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RevWLogString, call, "fuel");
        reverts_with_msg!(MatchEnum::RevWLogString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let error = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractLogs",
                project = "e2e/sway/logs/contract_logs",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "ContractLogs",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let lib_contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_instance.methods().produce_logs_values();

    let call_handler_2 = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&lib_contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let error = multi_call_handler
        .call::<((), ())>()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_script_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_loader_script_require_from_loader_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let contract_binary = "sway/contracts/lib_contract/out/release/lib_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;
    let contract_instance = MyContract::new(contract_id, wallet);

    let mut script_instance = script_instance;
    script_instance.convert_into_loader().await?;

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

fn assert_assert_eq_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left == right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

fn assert_assert_ne_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left != right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

#[tokio::test]
async fn test_contract_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/contracts/asserts"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        (($($arg: expr,)*), $method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        (($($arg: expr,)*), $method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!((32, 64,), assert_primitive, call, "assertion failed");
        reverts_with_msg!((32, 64,), assert_primitive, simulate, "assertion failed");
    }

    macro_rules! reverts_with_assert_eq_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_eq_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, call, "assertion failed");
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;
        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            simulate,
            "assertion failed"
        );
    }

    macro_rules! reverts_with_assert_ne_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_ne_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, call, "assertion failed");
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            simulate,
            "assertion failed"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_script_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_asserts"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    macro_rules! reverts_with_assert_eq_ne_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            call,
            "assertion failed"
        );
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            simulate,
            "assertion failed"
        );
    }
    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }

    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }

    Ok(())
}

#[tokio::test]
async fn contract_token_ops_error_messages() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/token_ops"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let contract_id = contract_instance.contract_id();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());
        let address = wallet.address();

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .simulate(Execution::Realistic)
            .await
            .expect_err("should return a revert error");
        assert_revert_containing_msg("failed transfer to address", error);

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .call()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("failed transfer to address", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_log_results() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let response = contract_instance
        .methods()
        .produce_bad_logs()
        .call()
        .await?;

    let log = response.decode_logs();

    let expected_err = format!(
        "codec: missing log formatter for log_id: `LogId({:?}, \"128\")`, data: `{:?}`. \
         Consider adding external contracts using `with_contracts()`",
        contract_instance.id().hash,
        [0u8; 8]
    );

    let succeeded = log.filter_succeeded();
    let failed = log.filter_failed();
    assert_eq!(succeeded, vec!["123".to_string()]);
    assert_eq!(failed.first().unwrap().to_string(), expected_err);

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_contract_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: decoding with too low max_tokens fails
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "Should have failed since there are more tokens than what is supported by default.",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "Should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Single call: increasing limits makes the test pass
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }
    {
        // Multi call: decoding with too low max_tokens will fail
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "should have failed since there are more tokens than what is supported by default",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Multi call: increasing limits makes the test pass
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_script_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_needs_custom_decoder_logging"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    {
        // Cannot decode the produced log with too low max_tokens
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response
            .decode_logs_with_type::<[u8; 1000]>()
            .expect_err("Cannot decode the log with default decoder config");

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty())
    }
    {
        // When the token limit is bumped log decoding succeeds
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty())
    }

    Ok(())
}

#[tokio::test]
async fn contract_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.produce_string_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let response = contract_methods.produce_string_log().call().await?;
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let response = contract_methods.produce_bytes_log().call().await?;
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let response = contract_methods.produce_raw_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let response = contract_methods.produce_vec_log().call().await?;
        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}

#[tokio::test]
async fn script_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_heap_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    let response = script_instance.main().call().await?;

    {
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}\n```

Configurable constants

Same as contracts, you can define configurable constants in scripts which can be changed during the script execution. Here is an example how the constants are defined.

```sway\nscript;

#[allow(dead_code)]
enum EnumWithGeneric<D> {
    VariantOne: D,
    VariantTwo: (),
}

struct StructWithGeneric<D> {
    field_1: D,
    field_2: u64,
}

configurable {
    BOOL: bool = true,
    U8: u8 = 8,
    U16: u16 = 16,
    U32: u32 = 32,
    U64: u64 = 63,
    U256: u256 = 0x0000000000000000000000000000000000000000000000000000000000000008u256,
    B256: b256 = 0x0101010101010101010101010101010101010101010101010101010101010101,
    STR_4: str[4] = __to_str_array("fuel"),
    TUPLE: (u8, bool) = (8, true),
    ARRAY: [u32; 3] = [253, 254, 255],
    STRUCT: StructWithGeneric<u8> = StructWithGeneric {
        field_1: 8,
        field_2: 16,
    },
    ENUM: EnumWithGeneric<bool> = EnumWithGeneric::VariantOne(true),
}
//U128: u128 = 128, //TODO: add once https://github.com/FuelLabs/sway/issues/5356 is done

fn main() -> (bool, u8, u16, u32, u64, u256, b256, str[4], (u8, bool), [u32; 3], StructWithGeneric<u8>, EnumWithGeneric<bool>) {
    (BOOL, U8, U16, U32, U64, U256, B256, STR_4, TUPLE, ARRAY, STRUCT, ENUM)
}\n```

Each configurable constant will get a dedicated with method in the SDK. For example, the constant STR_4 will get the with_STR_4 method which accepts the same type defined in sway. Below is an example where we chain several with methods and execute the script with the new constants.

```rust\nuse fuels::{
    core::codec::EncoderConfig,
    prelude::*,
    types::{Bits256, SizedAsciiString, U256},
};

#[tokio::test]
async fn contract_default_configurables() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/configurables/out/release/configurables-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/contracts/configurables/out/release/configurables.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .return_configurables()
        .call()
        .await?;

    let expected_value = (
        true,
        8,
        16,
        32,
        63,
        U256::from(8),
        Bits256([1; 32]),
        "fuel".try_into()?,
        (8, true),
        [253, 254, 255],
        StructWithGeneric {
            field_1: 8u8,
            field_2: 16,
        },
        EnumWithGeneric::VariantOne(true),
    );

    assert_eq!(response.value, expected_value);

    Ok(())
}

#[tokio::test]
async fn script_default_configurables() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_configurables"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let mut script_instance = script_instance;
    script_instance.convert_into_loader().await?;

    let response = script_instance.main().call().await?;

    let expected_value = (
        true,
        8,
        16,
        32,
        63,
        U256::from(8),
        Bits256([1; 32]),
        "fuel".try_into()?,
        (8, true),
        [253, 254, 255],
        StructWithGeneric {
            field_1: 8u8,
            field_2: 16,
        },
        EnumWithGeneric::VariantOne(true),
    );

    assert_eq!(response.value, expected_value);

    Ok(())
}

#[tokio::test]
async fn contract_configurables() -> Result<()> {
    // ANCHOR: contract_configurables
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/configurables/out/release/configurables-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;

    let str_4: SizedAsciiString<4> = "FUEL".try_into()?;
    let new_struct = StructWithGeneric {
        field_1: 16u8,
        field_2: 32,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyContractConfigurables::default()
        .with_BOOL(false)?
        .with_U8(7)?
        .with_U16(15)?
        .with_U32(31)?
        .with_U64(63)?
        .with_U256(U256::from(8))?
        .with_B256(Bits256([2; 32]))?
        .with_STR_4(str_4.clone())?
        .with_TUPLE((7, false))?
        .with_ARRAY([252, 253, 254])?
        .with_STRUCT(new_struct.clone())?
        .with_ENUM(new_enum.clone())?;

    let contract_id = Contract::load_from(
        "sway/contracts/configurables/out/release/configurables.bin",
        LoadConfiguration::default().with_configurables(configurables),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());
    // ANCHOR_END: contract_configurables

    let response = contract_instance
        .methods()
        .return_configurables()
        .call()
        .await?;

    let expected_value = (
        false,
        7,
        15,
        31,
        63,
        U256::from(8),
        Bits256([2; 32]),
        str_4,
        (7, false),
        [252, 253, 254],
        new_struct,
        new_enum,
    );

    assert_eq!(response.value, expected_value);

    Ok(())
}

#[tokio::test]
async fn contract_manual_configurables() -> Result<()> {
    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/configurables"
        )),
        Wallets("wallet")
    );

    let str_4: SizedAsciiString<4> = "FUEL".try_into()?;
    let new_struct = StructWithGeneric {
        field_1: 16u8,
        field_2: 32,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyContractConfigurables::default()
        .with_BOOL(false)?
        .with_U8(7)?
        .with_U16(15)?
        .with_U32(31)?
        .with_U64(63)?
        .with_U256(U256::from(8))?
        .with_B256(Bits256([2; 32]))?
        .with_STR_4(str_4.clone())?
        .with_TUPLE((7, false))?
        .with_ARRAY([252, 253, 254])?
        .with_STRUCT(new_struct.clone())?
        .with_ENUM(new_enum.clone())?;

    let contract_id = Contract::load_from(
        "sway/contracts/configurables/out/release/configurables.bin",
        LoadConfiguration::default(),
    )?
    .with_configurables(configurables)
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .return_configurables()
        .call()
        .await?;

    let expected_value = (
        false,
        7,
        15,
        31,
        63,
        U256::from(8),
        Bits256([2; 32]),
        str_4,
        (7, false),
        [252, 253, 254],
        new_struct,
        new_enum,
    );

    assert_eq!(response.value, expected_value);

    Ok(())
}

#[tokio::test]
async fn script_configurables() -> Result<()> {
    // ANCHOR: script_configurables
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/script_configurables/out/release/script_configurables-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/scripts/script_configurables/out/release/script_configurables.bin";
    let instance = MyScript::new(wallet, bin_path);

    let str_4: SizedAsciiString<4> = "FUEL".try_into()?;
    let new_struct = StructWithGeneric {
        field_1: 16u8,
        field_2: 32,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyScriptConfigurables::new(EncoderConfig {
        max_tokens: 5,
        ..Default::default()
    })
    .with_BOOL(false)?
    .with_U8(7)?
    .with_U16(15)?
    .with_U32(31)?
    .with_U64(63)?
    .with_U256(U256::from(8))?
    .with_B256(Bits256([2; 32]))?
    .with_STR_4(str_4.clone())?
    .with_TUPLE((7, false))?
    .with_ARRAY([252, 253, 254])?
    .with_STRUCT(new_struct.clone())?
    .with_ENUM(new_enum.clone())?;

    let response = instance
        .with_configurables(configurables)
        .main()
        .call()
        .await?;
    // ANCHOR_END: script_configurables

    let expected_value = (
        false,
        7,
        15,
        31,
        63,
        U256::from(8),
        Bits256([2; 32]),
        str_4,
        (7, false),
        [252, 253, 254],
        new_struct,
        new_enum,
    );

    assert_eq!(response.value, expected_value);

    Ok(())
}

#[tokio::test]
async fn configurable_encoder_config_is_applied() {
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/script_configurables/out/release/script_configurables-abi.json"
    ));

    let new_struct = StructWithGeneric {
        field_1: 16u8,
        field_2: 32,
    };

    {
        let _configurables = MyScriptConfigurables::default()
            .with_STRUCT(new_struct.clone())
            .expect("no encoder config, it works");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };

        // Fails when a wrong encoder config is set
        let configurables_error = MyScriptConfigurables::new(encoder_config)
            .with_STRUCT(new_struct)
            .expect_err("should error");

        assert!(configurables_error
            .to_string()
            .contains("token limit `1` reached while encoding. Try increasing it"),)
    }
}\n```

Predicates

Predicates, in Sway, are programs that return a Boolean value and do not have any side effects (they are pure). A predicate address can own assets. The predicate address is generated from the compiled byte code and is the same as the P2SH address used in Bitcoin. Users can seamlessly send assets to the predicate address as they do for any other address. To spend the predicate funds, the user has to provide the original byte code of the predicate together with the predicate data. The predicate data will be used when executing the byte code, and the funds can be transferred if the predicate is validated successfully.

Instantiating predicates

Let's consider the following predicate example:

```sway\npredicate;

fn main(a: u32, b: u64) -> bool {
    b == a.as_u64()
}\n```

We will look at a complete example of using the SDK to send and receive funds from a predicate.

First, we set up the wallets and a node instance. The call to the abigen! macro will generate all the types specified in the predicate plus two custom structs:

  • an encoder with an encode_data function that will conveniently encode all the arguments of the main function for us.
  • a configurables struct which holds methods for setting all the configurables mentioned in the predicate

Note: The abigen! macro will append Encoder and Configurables to the predicate's name field. Fox example, name="MyPredicate" will result in two structs called MyPredicateEncoder and MyPredicateConfigurables.

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        accounts::{predicate::Predicate, Account},
        crypto::{Message, SecretKey},
        prelude::*,
        types::B512,
    };

    #[tokio::test]
    async fn predicate_example() -> Result<()> {
        // ANCHOR: predicate_wallets
        let secret_key1: SecretKey =
            "0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301".parse()?;

        let secret_key2: SecretKey =
            "0x37fa81c84ccd547c30c176b118d5cb892bdb113e8e80141f266519422ef9eefd".parse()?;

        let secret_key3: SecretKey =
            "0x976e5c3fa620092c718d852ca703b6da9e3075b9f2ecb8ed42d9f746bf26aafb".parse()?;

        let mut wallet = WalletUnlocked::new_from_private_key(secret_key1, None);
        let mut wallet2 = WalletUnlocked::new_from_private_key(secret_key2, None);
        let mut wallet3 = WalletUnlocked::new_from_private_key(secret_key3, None);
        let mut receiver = WalletUnlocked::new_random(None);
        // ANCHOR_END: predicate_wallets

        // ANCHOR: predicate_coins
        let asset_id = AssetId::zeroed();
        let num_coins = 32;
        let amount = 64;
        let initial_balance = amount * num_coins;
        let all_coins = [&wallet, &wallet2, &wallet3, &receiver]
            .iter()
            .flat_map(|wallet| {
                setup_single_asset_coins(wallet.address(), asset_id, num_coins, amount)
            })
            .collect::<Vec<_>>();

        let provider = setup_test_provider(all_coins, vec![], None, None).await?;

        [&mut wallet, &mut wallet2, &mut wallet3, &mut receiver]
            .iter_mut()
            .for_each(|wallet| {
                wallet.set_provider(provider.clone());
            });
        // ANCHOR_END: predicate_coins

        let data_to_sign = Message::new([0; 32]);
        let signature1: B512 = wallet.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature2: B512 = wallet2.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature3: B512 = wallet3.sign(data_to_sign).await?.as_ref().try_into()?;

        let signatures = [signature1, signature2, signature3];

        // ANCHOR: predicate_load
        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/signatures/out/release/signatures-abi.json"
        ));

        let predicate_data = MyPredicateEncoder::default().encode_data(signatures)?;
        let code_path = "../../e2e/sway/predicates/signatures/out/release/signatures.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(provider)
            .with_data(predicate_data);
        // ANCHOR_END: predicate_load

        // ANCHOR: predicate_receive
        let amount_to_predicate = 500;

        wallet
            .transfer(
                predicate.address(),
                amount_to_predicate,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let predicate_balance = predicate.get_asset_balance(&asset_id).await?;
        assert_eq!(predicate_balance, amount_to_predicate);
        // ANCHOR_END: predicate_receive

        // ANCHOR: predicate_spend
        let amount_to_receiver = 300;
        predicate
            .transfer(
                receiver.address(),
                amount_to_receiver,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let receiver_balance_after = receiver.get_asset_balance(&asset_id).await?;
        assert_eq!(initial_balance + amount_to_receiver, receiver_balance_after);
        // ANCHOR_END: predicate_spend

        Ok(())
    }

    #[tokio::test]
    async fn predicate_data_example() -> Result<()> {
        // ANCHOR: predicate_data_setup
        let asset_id = AssetId::zeroed();
        let wallets_config = WalletsConfig::new_multiple_assets(
            2,
            vec![AssetConfig {
                id: asset_id,
                num_coins: 1,
                coin_amount: 1_000,
            }],
        );

        let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await?;

        let first_wallet = &wallets[0];
        let second_wallet = &wallets[1];

        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
        ));
        // ANCHOR_END: predicate_data_setup

        // ANCHOR: with_predicate_data
        let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;
        let code_path = "../../e2e/sway/predicates/basic_predicate/out/release/basic_predicate.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(first_wallet.try_provider()?.clone())
            .with_data(predicate_data);
        // ANCHOR_END: with_predicate_data

        // ANCHOR: predicate_data_lock_amount
        // First wallet transfers amount to predicate.
        first_wallet
            .transfer(predicate.address(), 500, asset_id, TxPolicies::default())
            .await?;

        // Check predicate balance.
        let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;

        assert_eq!(balance, 500);
        // ANCHOR_END: predicate_data_lock_amount

        // ANCHOR: predicate_data_unlock
        let amount_to_unlock = 300;

        predicate
            .transfer(
                second_wallet.address(),
                amount_to_unlock,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        // Second wallet balance is updated.
        let balance = second_wallet.get_asset_balance(&AssetId::zeroed()).await?;
        assert_eq!(balance, 1300);
        // ANCHOR_END: predicate_data_unlock
        Ok(())
    }
}\n```

Once we've compiled our predicate with forc build, we can create a Predicate instance via Predicate::load_from. The resulting data from encode_data can then be set on the loaded predicate.

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        accounts::{predicate::Predicate, Account},
        crypto::{Message, SecretKey},
        prelude::*,
        types::B512,
    };

    #[tokio::test]
    async fn predicate_example() -> Result<()> {
        // ANCHOR: predicate_wallets
        let secret_key1: SecretKey =
            "0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301".parse()?;

        let secret_key2: SecretKey =
            "0x37fa81c84ccd547c30c176b118d5cb892bdb113e8e80141f266519422ef9eefd".parse()?;

        let secret_key3: SecretKey =
            "0x976e5c3fa620092c718d852ca703b6da9e3075b9f2ecb8ed42d9f746bf26aafb".parse()?;

        let mut wallet = WalletUnlocked::new_from_private_key(secret_key1, None);
        let mut wallet2 = WalletUnlocked::new_from_private_key(secret_key2, None);
        let mut wallet3 = WalletUnlocked::new_from_private_key(secret_key3, None);
        let mut receiver = WalletUnlocked::new_random(None);
        // ANCHOR_END: predicate_wallets

        // ANCHOR: predicate_coins
        let asset_id = AssetId::zeroed();
        let num_coins = 32;
        let amount = 64;
        let initial_balance = amount * num_coins;
        let all_coins = [&wallet, &wallet2, &wallet3, &receiver]
            .iter()
            .flat_map(|wallet| {
                setup_single_asset_coins(wallet.address(), asset_id, num_coins, amount)
            })
            .collect::<Vec<_>>();

        let provider = setup_test_provider(all_coins, vec![], None, None).await?;

        [&mut wallet, &mut wallet2, &mut wallet3, &mut receiver]
            .iter_mut()
            .for_each(|wallet| {
                wallet.set_provider(provider.clone());
            });
        // ANCHOR_END: predicate_coins

        let data_to_sign = Message::new([0; 32]);
        let signature1: B512 = wallet.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature2: B512 = wallet2.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature3: B512 = wallet3.sign(data_to_sign).await?.as_ref().try_into()?;

        let signatures = [signature1, signature2, signature3];

        // ANCHOR: predicate_load
        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/signatures/out/release/signatures-abi.json"
        ));

        let predicate_data = MyPredicateEncoder::default().encode_data(signatures)?;
        let code_path = "../../e2e/sway/predicates/signatures/out/release/signatures.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(provider)
            .with_data(predicate_data);
        // ANCHOR_END: predicate_load

        // ANCHOR: predicate_receive
        let amount_to_predicate = 500;

        wallet
            .transfer(
                predicate.address(),
                amount_to_predicate,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let predicate_balance = predicate.get_asset_balance(&asset_id).await?;
        assert_eq!(predicate_balance, amount_to_predicate);
        // ANCHOR_END: predicate_receive

        // ANCHOR: predicate_spend
        let amount_to_receiver = 300;
        predicate
            .transfer(
                receiver.address(),
                amount_to_receiver,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let receiver_balance_after = receiver.get_asset_balance(&asset_id).await?;
        assert_eq!(initial_balance + amount_to_receiver, receiver_balance_after);
        // ANCHOR_END: predicate_spend

        Ok(())
    }

    #[tokio::test]
    async fn predicate_data_example() -> Result<()> {
        // ANCHOR: predicate_data_setup
        let asset_id = AssetId::zeroed();
        let wallets_config = WalletsConfig::new_multiple_assets(
            2,
            vec![AssetConfig {
                id: asset_id,
                num_coins: 1,
                coin_amount: 1_000,
            }],
        );

        let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await?;

        let first_wallet = &wallets[0];
        let second_wallet = &wallets[1];

        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
        ));
        // ANCHOR_END: predicate_data_setup

        // ANCHOR: with_predicate_data
        let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;
        let code_path = "../../e2e/sway/predicates/basic_predicate/out/release/basic_predicate.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(first_wallet.try_provider()?.clone())
            .with_data(predicate_data);
        // ANCHOR_END: with_predicate_data

        // ANCHOR: predicate_data_lock_amount
        // First wallet transfers amount to predicate.
        first_wallet
            .transfer(predicate.address(), 500, asset_id, TxPolicies::default())
            .await?;

        // Check predicate balance.
        let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;

        assert_eq!(balance, 500);
        // ANCHOR_END: predicate_data_lock_amount

        // ANCHOR: predicate_data_unlock
        let amount_to_unlock = 300;

        predicate
            .transfer(
                second_wallet.address(),
                amount_to_unlock,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        // Second wallet balance is updated.
        let balance = second_wallet.get_asset_balance(&AssetId::zeroed()).await?;
        assert_eq!(balance, 1300);
        // ANCHOR_END: predicate_data_unlock
        Ok(())
    }
}\n```

Next, we lock some assets in this predicate using the first wallet:

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        accounts::{predicate::Predicate, Account},
        crypto::{Message, SecretKey},
        prelude::*,
        types::B512,
    };

    #[tokio::test]
    async fn predicate_example() -> Result<()> {
        // ANCHOR: predicate_wallets
        let secret_key1: SecretKey =
            "0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301".parse()?;

        let secret_key2: SecretKey =
            "0x37fa81c84ccd547c30c176b118d5cb892bdb113e8e80141f266519422ef9eefd".parse()?;

        let secret_key3: SecretKey =
            "0x976e5c3fa620092c718d852ca703b6da9e3075b9f2ecb8ed42d9f746bf26aafb".parse()?;

        let mut wallet = WalletUnlocked::new_from_private_key(secret_key1, None);
        let mut wallet2 = WalletUnlocked::new_from_private_key(secret_key2, None);
        let mut wallet3 = WalletUnlocked::new_from_private_key(secret_key3, None);
        let mut receiver = WalletUnlocked::new_random(None);
        // ANCHOR_END: predicate_wallets

        // ANCHOR: predicate_coins
        let asset_id = AssetId::zeroed();
        let num_coins = 32;
        let amount = 64;
        let initial_balance = amount * num_coins;
        let all_coins = [&wallet, &wallet2, &wallet3, &receiver]
            .iter()
            .flat_map(|wallet| {
                setup_single_asset_coins(wallet.address(), asset_id, num_coins, amount)
            })
            .collect::<Vec<_>>();

        let provider = setup_test_provider(all_coins, vec![], None, None).await?;

        [&mut wallet, &mut wallet2, &mut wallet3, &mut receiver]
            .iter_mut()
            .for_each(|wallet| {
                wallet.set_provider(provider.clone());
            });
        // ANCHOR_END: predicate_coins

        let data_to_sign = Message::new([0; 32]);
        let signature1: B512 = wallet.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature2: B512 = wallet2.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature3: B512 = wallet3.sign(data_to_sign).await?.as_ref().try_into()?;

        let signatures = [signature1, signature2, signature3];

        // ANCHOR: predicate_load
        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/signatures/out/release/signatures-abi.json"
        ));

        let predicate_data = MyPredicateEncoder::default().encode_data(signatures)?;
        let code_path = "../../e2e/sway/predicates/signatures/out/release/signatures.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(provider)
            .with_data(predicate_data);
        // ANCHOR_END: predicate_load

        // ANCHOR: predicate_receive
        let amount_to_predicate = 500;

        wallet
            .transfer(
                predicate.address(),
                amount_to_predicate,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let predicate_balance = predicate.get_asset_balance(&asset_id).await?;
        assert_eq!(predicate_balance, amount_to_predicate);
        // ANCHOR_END: predicate_receive

        // ANCHOR: predicate_spend
        let amount_to_receiver = 300;
        predicate
            .transfer(
                receiver.address(),
                amount_to_receiver,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let receiver_balance_after = receiver.get_asset_balance(&asset_id).await?;
        assert_eq!(initial_balance + amount_to_receiver, receiver_balance_after);
        // ANCHOR_END: predicate_spend

        Ok(())
    }

    #[tokio::test]
    async fn predicate_data_example() -> Result<()> {
        // ANCHOR: predicate_data_setup
        let asset_id = AssetId::zeroed();
        let wallets_config = WalletsConfig::new_multiple_assets(
            2,
            vec![AssetConfig {
                id: asset_id,
                num_coins: 1,
                coin_amount: 1_000,
            }],
        );

        let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await?;

        let first_wallet = &wallets[0];
        let second_wallet = &wallets[1];

        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
        ));
        // ANCHOR_END: predicate_data_setup

        // ANCHOR: with_predicate_data
        let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;
        let code_path = "../../e2e/sway/predicates/basic_predicate/out/release/basic_predicate.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(first_wallet.try_provider()?.clone())
            .with_data(predicate_data);
        // ANCHOR_END: with_predicate_data

        // ANCHOR: predicate_data_lock_amount
        // First wallet transfers amount to predicate.
        first_wallet
            .transfer(predicate.address(), 500, asset_id, TxPolicies::default())
            .await?;

        // Check predicate balance.
        let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;

        assert_eq!(balance, 500);
        // ANCHOR_END: predicate_data_lock_amount

        // ANCHOR: predicate_data_unlock
        let amount_to_unlock = 300;

        predicate
            .transfer(
                second_wallet.address(),
                amount_to_unlock,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        // Second wallet balance is updated.
        let balance = second_wallet.get_asset_balance(&AssetId::zeroed()).await?;
        assert_eq!(balance, 1300);
        // ANCHOR_END: predicate_data_unlock
        Ok(())
    }
}\n```

Then we can transfer assets owned by the predicate via the Account trait:

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        accounts::{predicate::Predicate, Account},
        crypto::{Message, SecretKey},
        prelude::*,
        types::B512,
    };

    #[tokio::test]
    async fn predicate_example() -> Result<()> {
        // ANCHOR: predicate_wallets
        let secret_key1: SecretKey =
            "0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301".parse()?;

        let secret_key2: SecretKey =
            "0x37fa81c84ccd547c30c176b118d5cb892bdb113e8e80141f266519422ef9eefd".parse()?;

        let secret_key3: SecretKey =
            "0x976e5c3fa620092c718d852ca703b6da9e3075b9f2ecb8ed42d9f746bf26aafb".parse()?;

        let mut wallet = WalletUnlocked::new_from_private_key(secret_key1, None);
        let mut wallet2 = WalletUnlocked::new_from_private_key(secret_key2, None);
        let mut wallet3 = WalletUnlocked::new_from_private_key(secret_key3, None);
        let mut receiver = WalletUnlocked::new_random(None);
        // ANCHOR_END: predicate_wallets

        // ANCHOR: predicate_coins
        let asset_id = AssetId::zeroed();
        let num_coins = 32;
        let amount = 64;
        let initial_balance = amount * num_coins;
        let all_coins = [&wallet, &wallet2, &wallet3, &receiver]
            .iter()
            .flat_map(|wallet| {
                setup_single_asset_coins(wallet.address(), asset_id, num_coins, amount)
            })
            .collect::<Vec<_>>();

        let provider = setup_test_provider(all_coins, vec![], None, None).await?;

        [&mut wallet, &mut wallet2, &mut wallet3, &mut receiver]
            .iter_mut()
            .for_each(|wallet| {
                wallet.set_provider(provider.clone());
            });
        // ANCHOR_END: predicate_coins

        let data_to_sign = Message::new([0; 32]);
        let signature1: B512 = wallet.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature2: B512 = wallet2.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature3: B512 = wallet3.sign(data_to_sign).await?.as_ref().try_into()?;

        let signatures = [signature1, signature2, signature3];

        // ANCHOR: predicate_load
        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/signatures/out/release/signatures-abi.json"
        ));

        let predicate_data = MyPredicateEncoder::default().encode_data(signatures)?;
        let code_path = "../../e2e/sway/predicates/signatures/out/release/signatures.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(provider)
            .with_data(predicate_data);
        // ANCHOR_END: predicate_load

        // ANCHOR: predicate_receive
        let amount_to_predicate = 500;

        wallet
            .transfer(
                predicate.address(),
                amount_to_predicate,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let predicate_balance = predicate.get_asset_balance(&asset_id).await?;
        assert_eq!(predicate_balance, amount_to_predicate);
        // ANCHOR_END: predicate_receive

        // ANCHOR: predicate_spend
        let amount_to_receiver = 300;
        predicate
            .transfer(
                receiver.address(),
                amount_to_receiver,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let receiver_balance_after = receiver.get_asset_balance(&asset_id).await?;
        assert_eq!(initial_balance + amount_to_receiver, receiver_balance_after);
        // ANCHOR_END: predicate_spend

        Ok(())
    }

    #[tokio::test]
    async fn predicate_data_example() -> Result<()> {
        // ANCHOR: predicate_data_setup
        let asset_id = AssetId::zeroed();
        let wallets_config = WalletsConfig::new_multiple_assets(
            2,
            vec![AssetConfig {
                id: asset_id,
                num_coins: 1,
                coin_amount: 1_000,
            }],
        );

        let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await?;

        let first_wallet = &wallets[0];
        let second_wallet = &wallets[1];

        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
        ));
        // ANCHOR_END: predicate_data_setup

        // ANCHOR: with_predicate_data
        let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;
        let code_path = "../../e2e/sway/predicates/basic_predicate/out/release/basic_predicate.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(first_wallet.try_provider()?.clone())
            .with_data(predicate_data);
        // ANCHOR_END: with_predicate_data

        // ANCHOR: predicate_data_lock_amount
        // First wallet transfers amount to predicate.
        first_wallet
            .transfer(predicate.address(), 500, asset_id, TxPolicies::default())
            .await?;

        // Check predicate balance.
        let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;

        assert_eq!(balance, 500);
        // ANCHOR_END: predicate_data_lock_amount

        // ANCHOR: predicate_data_unlock
        let amount_to_unlock = 300;

        predicate
            .transfer(
                second_wallet.address(),
                amount_to_unlock,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        // Second wallet balance is updated.
        let balance = second_wallet.get_asset_balance(&AssetId::zeroed()).await?;
        assert_eq!(balance, 1300);
        // ANCHOR_END: predicate_data_unlock
        Ok(())
    }
}\n```

Configurable constants

Same as contracts and scripts, you can define configurable constants in predicates, which can be changed during the predicate execution. Here is an example of how the constants are defined.

```sway\npredicate;

impl Eq for StructWithGeneric<u8> {
    fn eq(self, other: Self) -> bool {
        self.field_1 == other.field_1 && self.field_2 == other.field_2
    }
}

impl Eq for EnumWithGeneric<bool> {
    fn eq(self, other: Self) -> bool {
        match (self, other) {
            (EnumWithGeneric::VariantOne, EnumWithGeneric::VariantOne) => true,
            (EnumWithGeneric::VariantTwo, EnumWithGeneric::VariantTwo) => true,
            _ => false,
        }
    }
}

// ANCHOR: predicate_configurables
#[allow(dead_code)]
enum EnumWithGeneric<D> {
    VariantOne: D,
    VariantTwo: (),
}

struct StructWithGeneric<D> {
    field_1: D,
    field_2: u64,
}

configurable {
    BOOL: bool = true,
    U8: u8 = 8,
    TUPLE: (u8, bool) = (8, true),
    ARRAY: [u32; 3] = [253, 254, 255],
    STRUCT: StructWithGeneric<u8> = StructWithGeneric {
        field_1: 8,
        field_2: 16,
    },
    ENUM: EnumWithGeneric<bool> = EnumWithGeneric::VariantOne(true),
}

fn main(
    switch: bool,
    u_8: u8,
    some_tuple: (u8, bool),
    some_array: [u32; 3],
    some_struct: StructWithGeneric<u8>,
    some_enum: EnumWithGeneric<bool>,
) -> bool {
    switch == BOOL && u_8 == U8 && some_tuple.0 == TUPLE.0 && some_tuple.1 == TUPLE.1 && some_array[0] == ARRAY[0] && some_array[1] == ARRAY[1] && some_array[2] == ARRAY[2] && some_struct == STRUCT && some_enum == ENUM
}
// ANCHOR_END: predicate_configurables\n```

Each configurable constant will get a dedicated with method in the SDK. For example, the constant U8 will get the with_U8 method which accepts the same type defined in sway. Below is an example where we chain several with methods and update the predicate with the new constants.

```rust\nuse std::default::Default;

use fuels::{
    core::{
        codec::{ABIEncoder, EncoderConfig},
        traits::Tokenizable,
    },
    prelude::*,
    programs::executable::Executable,
    types::{coin::Coin, coin_type::CoinType, input::Input, message::Message, output::Output},
};

async fn assert_address_balance(
    address: &Bech32Address,
    provider: &Provider,
    asset_id: AssetId,
    amount: u64,
) {
    let balance = provider
        .get_asset_balance(address, asset_id)
        .await
        .expect("Could not retrieve balance");
    assert_eq!(balance, amount);
}

fn get_test_coins_and_messages(
    address: &Bech32Address,
    num_coins: u64,
    num_messages: u64,
    amount: u64,
    start_nonce: u64,
) -> (Vec<Coin>, Vec<Message>, AssetId) {
    let asset_id = AssetId::zeroed();
    let coins = setup_single_asset_coins(address, asset_id, num_coins, amount);
    let messages = (0..num_messages)
        .map(|i| {
            setup_single_message(
                &Bech32Address::default(),
                address,
                amount,
                (start_nonce + i).into(),
                vec![],
            )
        })
        .collect();

    (coins, messages, asset_id)
}

fn get_test_message_w_data(address: &Bech32Address, amount: u64, nonce: u64) -> Message {
    setup_single_message(
        &Bech32Address::default(),
        address,
        amount,
        nonce.into(),
        vec![1, 2, 3],
    )
}

// Setup function used to assign coins and messages to a predicate address
// and create a `receiver` wallet
async fn setup_predicate_test(
    predicate_address: &Bech32Address,
    num_coins: u64,
    num_messages: u64,
    amount: u64,
) -> Result<(Provider, u64, WalletUnlocked, u64, AssetId, WalletUnlocked)> {
    let receiver_num_coins = 1;
    let receiver_amount = 1;
    let receiver_balance = receiver_num_coins * receiver_amount;

    let predicate_balance = (num_coins + num_messages) * amount;
    let mut receiver = WalletUnlocked::new_random(None);
    let mut extra_wallet = WalletUnlocked::new_random(None);

    let (mut coins, messages, asset_id) =
        get_test_coins_and_messages(predicate_address, num_coins, num_messages, amount, 0);

    coins.extend(setup_single_asset_coins(
        receiver.address(),
        asset_id,
        receiver_num_coins,
        receiver_amount,
    ));
    coins.extend(setup_single_asset_coins(
        extra_wallet.address(),
        AssetId::zeroed(),
        10_000,
        10_000,
    ));

    coins.extend(setup_single_asset_coins(
        predicate_address,
        AssetId::from([1u8; 32]),
        num_coins,
        amount,
    ));

    let provider = setup_test_provider(coins, messages, None, None).await?;
    receiver.set_provider(provider.clone());
    extra_wallet.set_provider(provider.clone());

    Ok((
        provider,
        predicate_balance,
        receiver,
        receiver_balance,
        asset_id,
        extra_wallet,
    ))
}

#[tokio::test]
async fn transfer_coins_and_messages_to_predicate() -> Result<()> {
    let num_coins = 16;
    let num_messages = 32;
    let amount = 64;
    let total_balance = (num_coins + num_messages) * amount;

    let mut wallet = WalletUnlocked::new_random(None);

    let (coins, messages, asset_id) =
        get_test_coins_and_messages(wallet.address(), num_coins, num_messages, amount, 0);

    let provider = setup_test_provider(coins, messages, None, None).await?;

    wallet.set_provider(provider.clone());

    let predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    wallet
        .transfer(
            predicate.address(),
            total_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has received the funds
    assert_address_balance(
        predicate.address(),
        &provider,
        asset_id,
        total_balance - expected_fee,
    )
    .await;
    Ok(())
}

#[tokio::test]
async fn spend_predicate_coins_messages_basic() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(4097, 4097)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn pay_with_predicate() -> Result<()> {
    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/types/predicates/u64/out/release/u64-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(32768)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/types/predicates/u64/out/release/u64.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();
    let tx_policies = TxPolicies::default()
        .with_tip(1)
        .with_script_gas_limit(1_000_000);

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    let consensus_parameters = provider.consensus_parameters().await?;
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        192 - expected_fee
    );

    let response = contract_methods
        .initialize_counter(42) // Build the ABI call
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    assert_eq!(42, response.value);
    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 2;
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        191 - expected_fee
    );

    Ok(())
}

#[tokio::test]
async fn pay_with_predicate_vector_data() -> Result<()> {
    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi =
                "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(12, 30, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();
    let tx_policies = TxPolicies::default()
        .with_tip(1)
        .with_script_gas_limit(1_000_000);

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    let consensus_parameters = provider.consensus_parameters().await?;
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        192 - expected_fee
    );

    let response = contract_methods
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 2;
    assert_eq!(42, response.value);
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        191 - expected_fee
    );

    Ok(())
}

#[tokio::test]
async fn predicate_contract_transfer() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(2, 40, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 300;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_balances = provider.get_contract_balances(&contract_id).await?;
    assert!(contract_balances.is_empty());

    let amount = 300;
    predicate
        .force_transfer_to_contract(
            &contract_id,
            amount,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await?;

    let contract_balances = predicate
        .try_provider()?
        .get_contract_balances(&contract_id)
        .await?;
    assert_eq!(contract_balances.len(), 1);

    let random_asset_balance = contract_balances.get(&AssetId::zeroed()).unwrap();
    assert_eq!(*random_asset_balance, 300);

    Ok(())
}

#[tokio::test]
async fn predicate_transfer_to_base_layer() -> Result<()> {
    use std::str::FromStr;

    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(22, 20, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 300;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let amount = 1000;
    let base_layer_address =
        Address::from_str("0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe")?;
    let base_layer_address = Bech32Address::from(base_layer_address);

    let (tx_id, msg_nonce, _receipts) = predicate
        .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
        .await?;

    // Create the next commit block to be able generate the proof
    provider.produce_blocks(1, None).await?;

    let proof = predicate
        .try_provider()?
        .get_message_proof(&tx_id, &msg_nonce, None, Some(2))
        .await?;

    assert_eq!(proof.amount, amount);
    assert_eq!(proof.recipient, base_layer_address);

    Ok(())
}

#[tokio::test]
async fn predicate_transfer_with_signed_resources() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(2, 40, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let predicate_num_coins = 4;
    let predicate_num_messages = 3;
    let predicate_amount = 1000;
    let predicate_balance = (predicate_num_coins + predicate_num_messages) * predicate_amount;

    let mut wallet = WalletUnlocked::new_random(None);
    let wallet_num_coins = 4;
    let wallet_num_messages = 3;
    let wallet_amount = 1000;
    let wallet_balance = (wallet_num_coins + wallet_num_messages) * wallet_amount;

    let (mut coins, mut messages, asset_id) = get_test_coins_and_messages(
        predicate.address(),
        predicate_num_coins,
        predicate_num_messages,
        predicate_amount,
        0,
    );
    let (wallet_coins, wallet_messages, _) = get_test_coins_and_messages(
        wallet.address(),
        wallet_num_coins,
        wallet_num_messages,
        wallet_amount,
        predicate_num_messages,
    );

    coins.extend(wallet_coins);
    messages.extend(wallet_messages);

    let provider = setup_test_provider(coins, messages, None, None).await?;
    wallet.set_provider(provider.clone());
    predicate.set_provider(provider.clone());

    let mut inputs = wallet
        .get_asset_inputs_for_amount(asset_id, wallet_balance, None)
        .await?;
    let predicate_inputs = predicate
        .get_asset_inputs_for_amount(asset_id, predicate_balance, None)
        .await?;
    inputs.extend(predicate_inputs);

    let outputs = vec![Output::change(predicate.address().into(), 0, asset_id)];

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, Default::default());
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(&provider).await?;

    provider.send_transaction_and_await_commit(tx).await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    assert_address_balance(
        predicate.address(),
        &provider,
        asset_id,
        predicate_balance + wallet_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn contract_tx_and_call_params_with_predicate() -> Result<()> {
    use fuels::prelude::*;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi =
                "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(22, 20, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 1;
    let num_messages = 1;
    let amount = 1000;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "./sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;
    println!("Contract deployed @ {contract_id}");

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();

    let tx_policies = TxPolicies::default().with_tip(100);

    let call_params_amount = 100;
    let call_params = CallParameters::default()
        .with_amount(call_params_amount)
        .with_asset_id(AssetId::zeroed());

    {
        let response = contract_methods
            .get_msg_amount()
            .with_tx_policies(tx_policies)
            .call_params(call_params.clone())?
            .call()
            .await?;

        // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
        let expected_fee = 2;
        assert_eq!(
            predicate.get_asset_balance(&AssetId::zeroed()).await?,
            1800 - expected_fee
        );
    }
    {
        let custom_asset = AssetId::from([1u8; 32]);

        let response = contract_methods
            .get_msg_amount()
            .call_params(call_params)?
            .add_custom_asset(custom_asset, 100, Some(Bech32Address::default()))
            .call()
            .await?;

        assert_eq!(predicate.get_asset_balance(&custom_asset).await?, 900);
    }

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn diff_asset_predicate_payment() -> Result<()> {
    use fuels::prelude::*;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi =
                "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(28, 14, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 1;
    let num_messages = 1;
    let amount = 1_000_000_000;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "./sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();

    let call_params = CallParameters::default()
        .with_amount(1_000_000)
        .with_asset_id(AssetId::from([1u8; 32]));

    let response = contract_methods
        .get_msg_amount()
        .call_params(call_params)?
        .call()
        .await?;

    Ok(())
}

#[tokio::test]
async fn predicate_default_configurables() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_configurables/out/release/predicate_configurables-abi.json"
    ));

    let new_struct = StructWithGeneric {
        field_1: 8u8,
        field_2: 16,
    };
    let new_enum = EnumWithGeneric::VariantOne(true);

    let predicate_data = MyPredicateEncoder::default().encode_data(
        true,
        8,
        (8, true),
        [253, 254, 255],
        new_struct,
        new_enum,
    )?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_configurables/out/release/predicate_configurables.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn predicate_configurables() -> Result<()> {
    // ANCHOR: predicate_configurables
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_configurables/out/release/predicate_configurables-abi.json"
    ));

    let new_tuple = (16, false);
    let new_array = [123, 124, 125];
    let new_struct = StructWithGeneric {
        field_1: 32u8,
        field_2: 64,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyPredicateConfigurables::default()
        .with_U8(8)?
        .with_TUPLE(new_tuple)?
        .with_ARRAY(new_array)?
        .with_STRUCT(new_struct.clone())?
        .with_ENUM(new_enum.clone())?;

    let predicate_data = MyPredicateEncoder::default()
        .encode_data(true, 8u8, new_tuple, new_array, new_struct, new_enum)?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_configurables/out/release/predicate_configurables.bin",
    )?
    .with_data(predicate_data)
    .with_configurables(configurables);
    // ANCHOR_END: predicate_configurables

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn predicate_adjust_fee_persists_message_w_data() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(4097, 4097)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let amount = 1000;
    let coins = setup_single_asset_coins(predicate.address(), AssetId::zeroed(), 1, amount);
    let message = get_test_message_w_data(predicate.address(), amount, Default::default());
    let message_input = Input::resource_predicate(
        CoinType::Message(message.clone()),
        predicate.code().to_vec(),
        predicate.data().to_vec(),
    );

    let provider = setup_test_provider(coins, vec![message.clone()], None, None).await?;
    predicate.set_provider(provider.clone());

    let mut tb = ScriptTransactionBuilder::prepare_transfer(
        vec![message_input.clone()],
        vec![],
        TxPolicies::default(),
    );
    predicate.adjust_for_fee(&mut tb, 0).await?;

    let tx = tb.build(&provider).await?;

    assert_eq!(tx.inputs().len(), 2);
    assert_eq!(tx.inputs()[0].message_id().unwrap(), message.message_id());

    Ok(())
}

#[tokio::test]
async fn predicate_transfer_non_base_asset() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(32, 32)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let mut wallet = WalletUnlocked::new_random(None);

    let amount = 5;
    let non_base_asset_id = AssetId::new([1; 32]);

    // wallet has base and predicate non base asset
    let mut coins = setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, amount);
    coins.extend(setup_single_asset_coins(
        predicate.address(),
        non_base_asset_id,
        1,
        amount,
    ));

    let provider = setup_test_provider(coins, vec![], None, None).await?;
    predicate.set_provider(provider.clone());
    wallet.set_provider(provider.clone());

    let inputs = predicate
        .get_asset_inputs_for_amount(non_base_asset_id, amount, None)
        .await?;
    let consensus_parameters = provider.consensus_parameters().await?;
    let outputs = vec![
        Output::change(wallet.address().into(), 0, non_base_asset_id),
        Output::change(
            wallet.address().into(),
            0,
            *consensus_parameters.base_asset_id(),
        ),
    ];

    let mut tb = ScriptTransactionBuilder::prepare_transfer(
        inputs,
        outputs,
        TxPolicies::default().with_tip(1),
    );

    tb.add_signer(wallet.clone())?;
    wallet.adjust_for_fee(&mut tb, 0).await?;

    let tx = tb.build(&provider).await?;

    provider
        .send_transaction_and_await_commit(tx)
        .await?
        .check(None)?;

    let wallet_balance = wallet.get_asset_balance(&non_base_asset_id).await?;

    assert_eq!(wallet_balance, amount);

    Ok(())
}

#[tokio::test]
async fn predicate_can_access_manually_added_witnesses() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_witnesses/out/release/predicate_witnesses-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(0, 1)?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_witnesses/out/release/predicate_witnesses.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 0;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let amount_to_send = 12;
    let inputs = predicate
        .get_asset_inputs_for_amount(asset_id, amount_to_send, None)
        .await?;
    let outputs =
        predicate.get_asset_outputs_for_amount(receiver.address(), asset_id, amount_to_send);

    let mut tx = ScriptTransactionBuilder::prepare_transfer(
        inputs,
        outputs,
        TxPolicies::default().with_witness_limit(32),
    )
    .build(&provider)
    .await?;

    let witness = ABIEncoder::default().encode(&[64u64.into_token()])?; // u64 because this is VM memory
    let witness2 = ABIEncoder::default().encode(&[4096u64.into_token()])?;

    tx.append_witness(witness.into())?;
    tx.append_witness(witness2.into())?;

    provider.send_transaction_and_await_commit(tx).await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    // The predicate has spent the funds
    assert_address_balance(
        predicate.address(),
        &provider,
        asset_id,
        predicate_balance - amount_to_send - expected_fee,
    )
    .await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + amount_to_send,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn tx_id_not_changed_after_adding_witnesses() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_witnesses/out/release/predicate_witnesses-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(0, 1)?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_witnesses/out/release/predicate_witnesses.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 0;
    let amount = 16;
    let (provider, _predicate_balance, receiver, _receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let amount_to_send = 12;
    let inputs = predicate
        .get_asset_inputs_for_amount(asset_id, amount_to_send, None)
        .await?;
    let outputs =
        predicate.get_asset_outputs_for_amount(receiver.address(), asset_id, amount_to_send);

    let mut tx = ScriptTransactionBuilder::prepare_transfer(
        inputs,
        outputs,
        TxPolicies::default().with_witness_limit(32),
    )
    .build(&provider)
    .await?;

    let consensus_parameters = provider.consensus_parameters().await?;
    let chain_id = consensus_parameters.chain_id();
    let tx_id = tx.id(chain_id);

    let witness = ABIEncoder::default().encode(&[64u64.into_token()])?; // u64 because this is VM memory
    let witness2 = ABIEncoder::default().encode(&[4096u64.into_token()])?;

    tx.append_witness(witness.into())?;
    tx.append_witness(witness2.into())?;
    let tx_id_after_witnesses = tx.id(chain_id);

    let tx_id_from_provider = provider.send_transaction(tx).await?;

    assert_eq!(tx_id, tx_id_after_witnesses);
    assert_eq!(tx_id, tx_id_from_provider);

    Ok(())
}

#[tokio::test]
async fn predicate_encoder_config_is_applied() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));
    {
        let _encoding_ok = MyPredicateEncoder::default()
            .encode_data(4097, 4097)
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let encoding_error = MyPredicateEncoder::new(encoder_config)
            .encode_data(4097, 4097)
            .expect_err("should fail");

        assert!(encoding_error
            .to_string()
            .contains("token limit `1` reached while encoding"));
    }

    Ok(())
}

#[tokio::test]
async fn predicate_transfers_non_base_asset() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(4097, 4097)?;
    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_message = 6;
    let amount = 20;
    let (provider, _, receiver, _, _, _) =
        setup_predicate_test(predicate.address(), num_coins, num_message, amount).await?;
    predicate.set_provider(provider);
    let other_asset_id = AssetId::from([1u8; 32]);

    let send_amount = num_coins * amount;
    predicate
        .transfer(
            receiver.address(),
            send_amount,
            other_asset_id,
            TxPolicies::default(),
        )
        .await?;

    assert_eq!(predicate.get_asset_balance(&other_asset_id).await?, 0,);

    assert_eq!(
        receiver.get_asset_balance(&other_asset_id).await?,
        send_amount,
    );

    Ok(())
}

#[tokio::test]
async fn predicate_with_invalid_data_fails() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(0, 100)?;
    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_message = 6;
    let amount = 20;
    let (provider, _, receiver, _, _, _) =
        setup_predicate_test(predicate.address(), num_coins, num_message, amount).await?;
    predicate.set_provider(provider);
    let other_asset_id = AssetId::from([1u8; 32]);

    let send_amount = num_coins * amount;
    let error_string = predicate
        .transfer(
            receiver.address(),
            send_amount,
            other_asset_id,
            TxPolicies::default(),
        )
        .await
        .unwrap_err()
        .to_string();

    assert!(error_string.contains("PredicateVerificationFailed(Panic(PredicateReturnedNonOne))"));
    assert_eq!(receiver.get_asset_balance(&other_asset_id).await?, 0);

    Ok(())
}

#[tokio::test]
async fn predicate_blobs() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_blobs/out/release/predicate_blobs-abi.json"
    ));

    // ANCHOR: preparing_the_predicate
    let configurables = MyPredicateConfigurables::default().with_SECRET_NUMBER(10001)?;

    let predicate_data = MyPredicateEncoder::default().encode_data(1, 19)?;

    let executable =
        Executable::load_from("sway/predicates/predicate_blobs/out/release/predicate_blobs.bin")?;

    let loader = executable
        .convert_to_loader()?
        .with_configurables(configurables);

    let mut predicate: Predicate = Predicate::from_code(loader.code()).with_data(predicate_data);
    // ANCHOR_END: preparing_the_predicate

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, extra_wallet) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    // we don't want to pay with the recipient wallet so that we don't affect the assertion we're
    // gonna make later on
    // ANCHOR: uploading_the_blob
    loader.upload_blob(extra_wallet).await?;

    predicate.set_provider(provider.clone());

    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;
    // ANCHOR_END: uploading_the_blob

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn predicate_configurables_in_blobs() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_configurables/out/release/predicate_configurables-abi.json"
    ));

    let new_tuple = (16, false);
    let new_array = [123, 124, 125];
    let new_struct = StructWithGeneric {
        field_1: 32u8,
        field_2: 64,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyPredicateConfigurables::default()
        .with_U8(8)?
        .with_TUPLE(new_tuple)?
        .with_ARRAY(new_array)?
        .with_STRUCT(new_struct.clone())?
        .with_ENUM(new_enum.clone())?;

    let predicate_data = MyPredicateEncoder::default()
        .encode_data(true, 8u8, new_tuple, new_array, new_struct, new_enum)?;

    let executable = Executable::load_from(
        "sway/predicates/predicate_configurables/out/release/predicate_configurables.bin",
    )?;

    let loader = executable
        .convert_to_loader()?
        .with_configurables(configurables);

    let mut predicate: Predicate = Predicate::from_code(loader.code()).with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, extra_wallet) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    loader.upload_blob(extra_wallet).await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}\n```

Signatures in predicates example

This is a more involved example where the predicate accepts three signatures and matches them to three predefined public keys. The ec_recover_address function is used to recover the public key from the signatures. If two of the three extracted public keys match the predefined public keys, the funds can be spent. Note that the signature order has to match the order of the predefined public keys.

```sway\npredicate;

use std::{b512::B512, constants::ZERO_B256, ecr::ec_recover_address, inputs::input_predicate_data};

fn extract_public_key_and_match(signature: B512, expected_public_key: b256) -> u64 {
    if let Result::Ok(pub_key_sig) = ec_recover_address(signature, ZERO_B256)
    {
        if pub_key_sig == Address::from(expected_public_key) {
            return 1;
        }
    }

    0
}

fn main(signatures: [B512; 3]) -> bool {
    let public_keys = [
        0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0,
        0x14df7c7e4e662db31fe2763b1734a3d680e7b743516319a49baaa22b2032a857,
        0x3ff494fb136978c3125844625dad6baf6e87cdb1328c8a51f35bda5afe72425c,
    ];

    let mut matched_keys = 0;

    matched_keys = extract_public_key_and_match(signatures[0], public_keys[0]);
    matched_keys = matched_keys + extract_public_key_and_match(signatures[1], public_keys[1]);
    matched_keys = matched_keys + extract_public_key_and_match(signatures[2], public_keys[2]);

    matched_keys > 1
}\n```

Let's use the SDK to interact with the predicate. First, let's create three wallets with specific keys. Their hashed public keys are already hard-coded in the predicate. Then we create the receiver wallet, which we will use to spend the predicate funds.

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        accounts::{predicate::Predicate, Account},
        crypto::{Message, SecretKey},
        prelude::*,
        types::B512,
    };

    #[tokio::test]
    async fn predicate_example() -> Result<()> {
        // ANCHOR: predicate_wallets
        let secret_key1: SecretKey =
            "0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301".parse()?;

        let secret_key2: SecretKey =
            "0x37fa81c84ccd547c30c176b118d5cb892bdb113e8e80141f266519422ef9eefd".parse()?;

        let secret_key3: SecretKey =
            "0x976e5c3fa620092c718d852ca703b6da9e3075b9f2ecb8ed42d9f746bf26aafb".parse()?;

        let mut wallet = WalletUnlocked::new_from_private_key(secret_key1, None);
        let mut wallet2 = WalletUnlocked::new_from_private_key(secret_key2, None);
        let mut wallet3 = WalletUnlocked::new_from_private_key(secret_key3, None);
        let mut receiver = WalletUnlocked::new_random(None);
        // ANCHOR_END: predicate_wallets

        // ANCHOR: predicate_coins
        let asset_id = AssetId::zeroed();
        let num_coins = 32;
        let amount = 64;
        let initial_balance = amount * num_coins;
        let all_coins = [&wallet, &wallet2, &wallet3, &receiver]
            .iter()
            .flat_map(|wallet| {
                setup_single_asset_coins(wallet.address(), asset_id, num_coins, amount)
            })
            .collect::<Vec<_>>();

        let provider = setup_test_provider(all_coins, vec![], None, None).await?;

        [&mut wallet, &mut wallet2, &mut wallet3, &mut receiver]
            .iter_mut()
            .for_each(|wallet| {
                wallet.set_provider(provider.clone());
            });
        // ANCHOR_END: predicate_coins

        let data_to_sign = Message::new([0; 32]);
        let signature1: B512 = wallet.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature2: B512 = wallet2.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature3: B512 = wallet3.sign(data_to_sign).await?.as_ref().try_into()?;

        let signatures = [signature1, signature2, signature3];

        // ANCHOR: predicate_load
        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/signatures/out/release/signatures-abi.json"
        ));

        let predicate_data = MyPredicateEncoder::default().encode_data(signatures)?;
        let code_path = "../../e2e/sway/predicates/signatures/out/release/signatures.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(provider)
            .with_data(predicate_data);
        // ANCHOR_END: predicate_load

        // ANCHOR: predicate_receive
        let amount_to_predicate = 500;

        wallet
            .transfer(
                predicate.address(),
                amount_to_predicate,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let predicate_balance = predicate.get_asset_balance(&asset_id).await?;
        assert_eq!(predicate_balance, amount_to_predicate);
        // ANCHOR_END: predicate_receive

        // ANCHOR: predicate_spend
        let amount_to_receiver = 300;
        predicate
            .transfer(
                receiver.address(),
                amount_to_receiver,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let receiver_balance_after = receiver.get_asset_balance(&asset_id).await?;
        assert_eq!(initial_balance + amount_to_receiver, receiver_balance_after);
        // ANCHOR_END: predicate_spend

        Ok(())
    }

    #[tokio::test]
    async fn predicate_data_example() -> Result<()> {
        // ANCHOR: predicate_data_setup
        let asset_id = AssetId::zeroed();
        let wallets_config = WalletsConfig::new_multiple_assets(
            2,
            vec![AssetConfig {
                id: asset_id,
                num_coins: 1,
                coin_amount: 1_000,
            }],
        );

        let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await?;

        let first_wallet = &wallets[0];
        let second_wallet = &wallets[1];

        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
        ));
        // ANCHOR_END: predicate_data_setup

        // ANCHOR: with_predicate_data
        let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;
        let code_path = "../../e2e/sway/predicates/basic_predicate/out/release/basic_predicate.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(first_wallet.try_provider()?.clone())
            .with_data(predicate_data);
        // ANCHOR_END: with_predicate_data

        // ANCHOR: predicate_data_lock_amount
        // First wallet transfers amount to predicate.
        first_wallet
            .transfer(predicate.address(), 500, asset_id, TxPolicies::default())
            .await?;

        // Check predicate balance.
        let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;

        assert_eq!(balance, 500);
        // ANCHOR_END: predicate_data_lock_amount

        // ANCHOR: predicate_data_unlock
        let amount_to_unlock = 300;

        predicate
            .transfer(
                second_wallet.address(),
                amount_to_unlock,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        // Second wallet balance is updated.
        let balance = second_wallet.get_asset_balance(&AssetId::zeroed()).await?;
        assert_eq!(balance, 1300);
        // ANCHOR_END: predicate_data_unlock
        Ok(())
    }
}\n```

Next, let's add some coins, start a provider and connect it with the wallets.

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        accounts::{predicate::Predicate, Account},
        crypto::{Message, SecretKey},
        prelude::*,
        types::B512,
    };

    #[tokio::test]
    async fn predicate_example() -> Result<()> {
        // ANCHOR: predicate_wallets
        let secret_key1: SecretKey =
            "0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301".parse()?;

        let secret_key2: SecretKey =
            "0x37fa81c84ccd547c30c176b118d5cb892bdb113e8e80141f266519422ef9eefd".parse()?;

        let secret_key3: SecretKey =
            "0x976e5c3fa620092c718d852ca703b6da9e3075b9f2ecb8ed42d9f746bf26aafb".parse()?;

        let mut wallet = WalletUnlocked::new_from_private_key(secret_key1, None);
        let mut wallet2 = WalletUnlocked::new_from_private_key(secret_key2, None);
        let mut wallet3 = WalletUnlocked::new_from_private_key(secret_key3, None);
        let mut receiver = WalletUnlocked::new_random(None);
        // ANCHOR_END: predicate_wallets

        // ANCHOR: predicate_coins
        let asset_id = AssetId::zeroed();
        let num_coins = 32;
        let amount = 64;
        let initial_balance = amount * num_coins;
        let all_coins = [&wallet, &wallet2, &wallet3, &receiver]
            .iter()
            .flat_map(|wallet| {
                setup_single_asset_coins(wallet.address(), asset_id, num_coins, amount)
            })
            .collect::<Vec<_>>();

        let provider = setup_test_provider(all_coins, vec![], None, None).await?;

        [&mut wallet, &mut wallet2, &mut wallet3, &mut receiver]
            .iter_mut()
            .for_each(|wallet| {
                wallet.set_provider(provider.clone());
            });
        // ANCHOR_END: predicate_coins

        let data_to_sign = Message::new([0; 32]);
        let signature1: B512 = wallet.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature2: B512 = wallet2.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature3: B512 = wallet3.sign(data_to_sign).await?.as_ref().try_into()?;

        let signatures = [signature1, signature2, signature3];

        // ANCHOR: predicate_load
        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/signatures/out/release/signatures-abi.json"
        ));

        let predicate_data = MyPredicateEncoder::default().encode_data(signatures)?;
        let code_path = "../../e2e/sway/predicates/signatures/out/release/signatures.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(provider)
            .with_data(predicate_data);
        // ANCHOR_END: predicate_load

        // ANCHOR: predicate_receive
        let amount_to_predicate = 500;

        wallet
            .transfer(
                predicate.address(),
                amount_to_predicate,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let predicate_balance = predicate.get_asset_balance(&asset_id).await?;
        assert_eq!(predicate_balance, amount_to_predicate);
        // ANCHOR_END: predicate_receive

        // ANCHOR: predicate_spend
        let amount_to_receiver = 300;
        predicate
            .transfer(
                receiver.address(),
                amount_to_receiver,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let receiver_balance_after = receiver.get_asset_balance(&asset_id).await?;
        assert_eq!(initial_balance + amount_to_receiver, receiver_balance_after);
        // ANCHOR_END: predicate_spend

        Ok(())
    }

    #[tokio::test]
    async fn predicate_data_example() -> Result<()> {
        // ANCHOR: predicate_data_setup
        let asset_id = AssetId::zeroed();
        let wallets_config = WalletsConfig::new_multiple_assets(
            2,
            vec![AssetConfig {
                id: asset_id,
                num_coins: 1,
                coin_amount: 1_000,
            }],
        );

        let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await?;

        let first_wallet = &wallets[0];
        let second_wallet = &wallets[1];

        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
        ));
        // ANCHOR_END: predicate_data_setup

        // ANCHOR: with_predicate_data
        let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;
        let code_path = "../../e2e/sway/predicates/basic_predicate/out/release/basic_predicate.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(first_wallet.try_provider()?.clone())
            .with_data(predicate_data);
        // ANCHOR_END: with_predicate_data

        // ANCHOR: predicate_data_lock_amount
        // First wallet transfers amount to predicate.
        first_wallet
            .transfer(predicate.address(), 500, asset_id, TxPolicies::default())
            .await?;

        // Check predicate balance.
        let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;

        assert_eq!(balance, 500);
        // ANCHOR_END: predicate_data_lock_amount

        // ANCHOR: predicate_data_unlock
        let amount_to_unlock = 300;

        predicate
            .transfer(
                second_wallet.address(),
                amount_to_unlock,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        // Second wallet balance is updated.
        let balance = second_wallet.get_asset_balance(&AssetId::zeroed()).await?;
        assert_eq!(balance, 1300);
        // ANCHOR_END: predicate_data_unlock
        Ok(())
    }
}\n```

Now we can use the predicate abigen to create a predicate encoder instance for us. To spend the funds now locked in the predicate, we must provide two out of three signatures whose public keys match the ones we defined in the predicate. In this example, the signatures are generated from an array of zeros.

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        accounts::{predicate::Predicate, Account},
        crypto::{Message, SecretKey},
        prelude::*,
        types::B512,
    };

    #[tokio::test]
    async fn predicate_example() -> Result<()> {
        // ANCHOR: predicate_wallets
        let secret_key1: SecretKey =
            "0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301".parse()?;

        let secret_key2: SecretKey =
            "0x37fa81c84ccd547c30c176b118d5cb892bdb113e8e80141f266519422ef9eefd".parse()?;

        let secret_key3: SecretKey =
            "0x976e5c3fa620092c718d852ca703b6da9e3075b9f2ecb8ed42d9f746bf26aafb".parse()?;

        let mut wallet = WalletUnlocked::new_from_private_key(secret_key1, None);
        let mut wallet2 = WalletUnlocked::new_from_private_key(secret_key2, None);
        let mut wallet3 = WalletUnlocked::new_from_private_key(secret_key3, None);
        let mut receiver = WalletUnlocked::new_random(None);
        // ANCHOR_END: predicate_wallets

        // ANCHOR: predicate_coins
        let asset_id = AssetId::zeroed();
        let num_coins = 32;
        let amount = 64;
        let initial_balance = amount * num_coins;
        let all_coins = [&wallet, &wallet2, &wallet3, &receiver]
            .iter()
            .flat_map(|wallet| {
                setup_single_asset_coins(wallet.address(), asset_id, num_coins, amount)
            })
            .collect::<Vec<_>>();

        let provider = setup_test_provider(all_coins, vec![], None, None).await?;

        [&mut wallet, &mut wallet2, &mut wallet3, &mut receiver]
            .iter_mut()
            .for_each(|wallet| {
                wallet.set_provider(provider.clone());
            });
        // ANCHOR_END: predicate_coins

        let data_to_sign = Message::new([0; 32]);
        let signature1: B512 = wallet.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature2: B512 = wallet2.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature3: B512 = wallet3.sign(data_to_sign).await?.as_ref().try_into()?;

        let signatures = [signature1, signature2, signature3];

        // ANCHOR: predicate_load
        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/signatures/out/release/signatures-abi.json"
        ));

        let predicate_data = MyPredicateEncoder::default().encode_data(signatures)?;
        let code_path = "../../e2e/sway/predicates/signatures/out/release/signatures.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(provider)
            .with_data(predicate_data);
        // ANCHOR_END: predicate_load

        // ANCHOR: predicate_receive
        let amount_to_predicate = 500;

        wallet
            .transfer(
                predicate.address(),
                amount_to_predicate,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let predicate_balance = predicate.get_asset_balance(&asset_id).await?;
        assert_eq!(predicate_balance, amount_to_predicate);
        // ANCHOR_END: predicate_receive

        // ANCHOR: predicate_spend
        let amount_to_receiver = 300;
        predicate
            .transfer(
                receiver.address(),
                amount_to_receiver,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let receiver_balance_after = receiver.get_asset_balance(&asset_id).await?;
        assert_eq!(initial_balance + amount_to_receiver, receiver_balance_after);
        // ANCHOR_END: predicate_spend

        Ok(())
    }

    #[tokio::test]
    async fn predicate_data_example() -> Result<()> {
        // ANCHOR: predicate_data_setup
        let asset_id = AssetId::zeroed();
        let wallets_config = WalletsConfig::new_multiple_assets(
            2,
            vec![AssetConfig {
                id: asset_id,
                num_coins: 1,
                coin_amount: 1_000,
            }],
        );

        let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await?;

        let first_wallet = &wallets[0];
        let second_wallet = &wallets[1];

        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
        ));
        // ANCHOR_END: predicate_data_setup

        // ANCHOR: with_predicate_data
        let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;
        let code_path = "../../e2e/sway/predicates/basic_predicate/out/release/basic_predicate.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(first_wallet.try_provider()?.clone())
            .with_data(predicate_data);
        // ANCHOR_END: with_predicate_data

        // ANCHOR: predicate_data_lock_amount
        // First wallet transfers amount to predicate.
        first_wallet
            .transfer(predicate.address(), 500, asset_id, TxPolicies::default())
            .await?;

        // Check predicate balance.
        let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;

        assert_eq!(balance, 500);
        // ANCHOR_END: predicate_data_lock_amount

        // ANCHOR: predicate_data_unlock
        let amount_to_unlock = 300;

        predicate
            .transfer(
                second_wallet.address(),
                amount_to_unlock,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        // Second wallet balance is updated.
        let balance = second_wallet.get_asset_balance(&AssetId::zeroed()).await?;
        assert_eq!(balance, 1300);
        // ANCHOR_END: predicate_data_unlock
        Ok(())
    }
}\n```

Next, we transfer some assets from a wallet to the created predicate. We also confirm that the funds are indeed transferred.

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        accounts::{predicate::Predicate, Account},
        crypto::{Message, SecretKey},
        prelude::*,
        types::B512,
    };

    #[tokio::test]
    async fn predicate_example() -> Result<()> {
        // ANCHOR: predicate_wallets
        let secret_key1: SecretKey =
            "0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301".parse()?;

        let secret_key2: SecretKey =
            "0x37fa81c84ccd547c30c176b118d5cb892bdb113e8e80141f266519422ef9eefd".parse()?;

        let secret_key3: SecretKey =
            "0x976e5c3fa620092c718d852ca703b6da9e3075b9f2ecb8ed42d9f746bf26aafb".parse()?;

        let mut wallet = WalletUnlocked::new_from_private_key(secret_key1, None);
        let mut wallet2 = WalletUnlocked::new_from_private_key(secret_key2, None);
        let mut wallet3 = WalletUnlocked::new_from_private_key(secret_key3, None);
        let mut receiver = WalletUnlocked::new_random(None);
        // ANCHOR_END: predicate_wallets

        // ANCHOR: predicate_coins
        let asset_id = AssetId::zeroed();
        let num_coins = 32;
        let amount = 64;
        let initial_balance = amount * num_coins;
        let all_coins = [&wallet, &wallet2, &wallet3, &receiver]
            .iter()
            .flat_map(|wallet| {
                setup_single_asset_coins(wallet.address(), asset_id, num_coins, amount)
            })
            .collect::<Vec<_>>();

        let provider = setup_test_provider(all_coins, vec![], None, None).await?;

        [&mut wallet, &mut wallet2, &mut wallet3, &mut receiver]
            .iter_mut()
            .for_each(|wallet| {
                wallet.set_provider(provider.clone());
            });
        // ANCHOR_END: predicate_coins

        let data_to_sign = Message::new([0; 32]);
        let signature1: B512 = wallet.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature2: B512 = wallet2.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature3: B512 = wallet3.sign(data_to_sign).await?.as_ref().try_into()?;

        let signatures = [signature1, signature2, signature3];

        // ANCHOR: predicate_load
        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/signatures/out/release/signatures-abi.json"
        ));

        let predicate_data = MyPredicateEncoder::default().encode_data(signatures)?;
        let code_path = "../../e2e/sway/predicates/signatures/out/release/signatures.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(provider)
            .with_data(predicate_data);
        // ANCHOR_END: predicate_load

        // ANCHOR: predicate_receive
        let amount_to_predicate = 500;

        wallet
            .transfer(
                predicate.address(),
                amount_to_predicate,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let predicate_balance = predicate.get_asset_balance(&asset_id).await?;
        assert_eq!(predicate_balance, amount_to_predicate);
        // ANCHOR_END: predicate_receive

        // ANCHOR: predicate_spend
        let amount_to_receiver = 300;
        predicate
            .transfer(
                receiver.address(),
                amount_to_receiver,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let receiver_balance_after = receiver.get_asset_balance(&asset_id).await?;
        assert_eq!(initial_balance + amount_to_receiver, receiver_balance_after);
        // ANCHOR_END: predicate_spend

        Ok(())
    }

    #[tokio::test]
    async fn predicate_data_example() -> Result<()> {
        // ANCHOR: predicate_data_setup
        let asset_id = AssetId::zeroed();
        let wallets_config = WalletsConfig::new_multiple_assets(
            2,
            vec![AssetConfig {
                id: asset_id,
                num_coins: 1,
                coin_amount: 1_000,
            }],
        );

        let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await?;

        let first_wallet = &wallets[0];
        let second_wallet = &wallets[1];

        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
        ));
        // ANCHOR_END: predicate_data_setup

        // ANCHOR: with_predicate_data
        let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;
        let code_path = "../../e2e/sway/predicates/basic_predicate/out/release/basic_predicate.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(first_wallet.try_provider()?.clone())
            .with_data(predicate_data);
        // ANCHOR_END: with_predicate_data

        // ANCHOR: predicate_data_lock_amount
        // First wallet transfers amount to predicate.
        first_wallet
            .transfer(predicate.address(), 500, asset_id, TxPolicies::default())
            .await?;

        // Check predicate balance.
        let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;

        assert_eq!(balance, 500);
        // ANCHOR_END: predicate_data_lock_amount

        // ANCHOR: predicate_data_unlock
        let amount_to_unlock = 300;

        predicate
            .transfer(
                second_wallet.address(),
                amount_to_unlock,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        // Second wallet balance is updated.
        let balance = second_wallet.get_asset_balance(&AssetId::zeroed()).await?;
        assert_eq!(balance, 1300);
        // ANCHOR_END: predicate_data_unlock
        Ok(())
    }
}\n```

We can use the transfer method from the Account trait to transfer the assets. If the predicate data is correct, the receiver wallet will get the funds, and we will verify that the amount is correct.

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        accounts::{predicate::Predicate, Account},
        crypto::{Message, SecretKey},
        prelude::*,
        types::B512,
    };

    #[tokio::test]
    async fn predicate_example() -> Result<()> {
        // ANCHOR: predicate_wallets
        let secret_key1: SecretKey =
            "0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301".parse()?;

        let secret_key2: SecretKey =
            "0x37fa81c84ccd547c30c176b118d5cb892bdb113e8e80141f266519422ef9eefd".parse()?;

        let secret_key3: SecretKey =
            "0x976e5c3fa620092c718d852ca703b6da9e3075b9f2ecb8ed42d9f746bf26aafb".parse()?;

        let mut wallet = WalletUnlocked::new_from_private_key(secret_key1, None);
        let mut wallet2 = WalletUnlocked::new_from_private_key(secret_key2, None);
        let mut wallet3 = WalletUnlocked::new_from_private_key(secret_key3, None);
        let mut receiver = WalletUnlocked::new_random(None);
        // ANCHOR_END: predicate_wallets

        // ANCHOR: predicate_coins
        let asset_id = AssetId::zeroed();
        let num_coins = 32;
        let amount = 64;
        let initial_balance = amount * num_coins;
        let all_coins = [&wallet, &wallet2, &wallet3, &receiver]
            .iter()
            .flat_map(|wallet| {
                setup_single_asset_coins(wallet.address(), asset_id, num_coins, amount)
            })
            .collect::<Vec<_>>();

        let provider = setup_test_provider(all_coins, vec![], None, None).await?;

        [&mut wallet, &mut wallet2, &mut wallet3, &mut receiver]
            .iter_mut()
            .for_each(|wallet| {
                wallet.set_provider(provider.clone());
            });
        // ANCHOR_END: predicate_coins

        let data_to_sign = Message::new([0; 32]);
        let signature1: B512 = wallet.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature2: B512 = wallet2.sign(data_to_sign).await?.as_ref().try_into()?;
        let signature3: B512 = wallet3.sign(data_to_sign).await?.as_ref().try_into()?;

        let signatures = [signature1, signature2, signature3];

        // ANCHOR: predicate_load
        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/signatures/out/release/signatures-abi.json"
        ));

        let predicate_data = MyPredicateEncoder::default().encode_data(signatures)?;
        let code_path = "../../e2e/sway/predicates/signatures/out/release/signatures.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(provider)
            .with_data(predicate_data);
        // ANCHOR_END: predicate_load

        // ANCHOR: predicate_receive
        let amount_to_predicate = 500;

        wallet
            .transfer(
                predicate.address(),
                amount_to_predicate,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let predicate_balance = predicate.get_asset_balance(&asset_id).await?;
        assert_eq!(predicate_balance, amount_to_predicate);
        // ANCHOR_END: predicate_receive

        // ANCHOR: predicate_spend
        let amount_to_receiver = 300;
        predicate
            .transfer(
                receiver.address(),
                amount_to_receiver,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let receiver_balance_after = receiver.get_asset_balance(&asset_id).await?;
        assert_eq!(initial_balance + amount_to_receiver, receiver_balance_after);
        // ANCHOR_END: predicate_spend

        Ok(())
    }

    #[tokio::test]
    async fn predicate_data_example() -> Result<()> {
        // ANCHOR: predicate_data_setup
        let asset_id = AssetId::zeroed();
        let wallets_config = WalletsConfig::new_multiple_assets(
            2,
            vec![AssetConfig {
                id: asset_id,
                num_coins: 1,
                coin_amount: 1_000,
            }],
        );

        let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await?;

        let first_wallet = &wallets[0];
        let second_wallet = &wallets[1];

        abigen!(Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
        ));
        // ANCHOR_END: predicate_data_setup

        // ANCHOR: with_predicate_data
        let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;
        let code_path = "../../e2e/sway/predicates/basic_predicate/out/release/basic_predicate.bin";

        let predicate: Predicate = Predicate::load_from(code_path)?
            .with_provider(first_wallet.try_provider()?.clone())
            .with_data(predicate_data);
        // ANCHOR_END: with_predicate_data

        // ANCHOR: predicate_data_lock_amount
        // First wallet transfers amount to predicate.
        first_wallet
            .transfer(predicate.address(), 500, asset_id, TxPolicies::default())
            .await?;

        // Check predicate balance.
        let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;

        assert_eq!(balance, 500);
        // ANCHOR_END: predicate_data_lock_amount

        // ANCHOR: predicate_data_unlock
        let amount_to_unlock = 300;

        predicate
            .transfer(
                second_wallet.address(),
                amount_to_unlock,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        // Second wallet balance is updated.
        let balance = second_wallet.get_asset_balance(&AssetId::zeroed()).await?;
        assert_eq!(balance, 1300);
        // ANCHOR_END: predicate_data_unlock
        Ok(())
    }
}\n```

Pre-uploading code

If you have a script or predicate that is larger than normal or which you plan on calling often, you can pre-upload its code as a blob to the network and run a loader script/predicate instead. The loader can be configured with the script/predicate configurables, so you can change how the script/predicate is configured on each run without having large transactions due to the code duplication.

Scripts

A high level pre-upload:

```rust\nuse std::time::Duration;

use fuel_tx::Output;
use fuels::{
    client::{PageDirection, PaginationRequest},
    core::{
        codec::{DecoderConfig, EncoderConfig},
        traits::Tokenizable,
        Configurables,
    },
    prelude::*,
    programs::{executable::Executable, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE},
    types::{Bits256, Identity},
};

#[tokio::test]
async fn main_function_arguments() -> Result<()> {
    // ANCHOR: script_with_arguments
    // The abigen is used for the same purpose as with contracts (Rust bindings)
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/scripts/arguments/out/release/arguments.bin";
    let script_instance = MyScript::new(wallet, bin_path);

    let bim = Bimbam { val: 90 };
    let bam = SugarySnack {
        twix: 100,
        mars: 1000,
    };

    let result = script_instance.main(bim, bam).call().await?;

    let expected = Bimbam { val: 2190 };
    assert_eq!(result.value, expected);
    // ANCHOR_END: script_with_arguments
    Ok(())
}

#[tokio::test]
async fn script_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let a = 4u64;
    let b = 2u32;
    let estimated_gas_used = script_instance
        .main(a, b)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = script_instance.main(a, b).call().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);

    Ok(())
}

#[tokio::test]
async fn test_basic_script_with_tx_policies() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "bimbam_script",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "bimbam_script",
            wallet = "wallet"
        )
    );

    let a = 1000u64;
    let b = 2000u32;
    let result = script_instance.main(a, b).call().await?;
    assert_eq!(result.value, "hello");

    // ANCHOR: script_with_tx_policies
    let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
    let result = script_instance
        .main(a, b)
        .with_tx_policies(tx_policies)
        .call()
        .await?;
    // ANCHOR_END: script_with_tx_policies
    assert_eq!(result.value, "hello");

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "transfer_script",
            project = "e2e/sway/scripts/transfer_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "transfer_script",
            wallet = "wallet"
        )
    );

    let provider = wallet.try_provider()?.clone();
    let mut receiver = WalletUnlocked::new_random(None);
    receiver.set_provider(provider);

    let amount = 1000;
    let asset_id = AssetId::zeroed();
    let script_call = script_instance.main(
        amount,
        asset_id,
        Identity::Address(receiver.address().into()),
    );
    let inputs = wallet
        .get_asset_inputs_for_amount(asset_id, amount, None)
        .await?;
    let output = Output::change(wallet.address().into(), 0, asset_id);
    let _ = script_call
        .with_inputs(inputs)
        .with_outputs(vec![output])
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    let receiver_balance = receiver.get_asset_balance(&asset_id).await?;
    assert_eq!(receiver_balance, amount);

    Ok(())
}

#[tokio::test]
async fn test_script_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };
    let response = script_instance.main(my_struct).call().await?;

    assert_eq!(response.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_enum"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_enum = MyEnum::Two;
    let response = script_instance.main(my_enum).call().await?;

    assert_eq!(response.value, 2);
    Ok(())
}

#[tokio::test]
async fn test_script_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_array"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_array: [u64; 4] = [1, 2, 3, 4];
    let response = script_instance.main(my_array).call().await?;

    assert_eq!(response.value, 10);
    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_on_script_call() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_needs_custom_decoder"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    {
        // Will fail if max_tokens too low
        script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 101,
                ..Default::default()
            })
            .call()
            .await
            .expect_err(
                "Should fail because return type has more tokens than what is allowed by default",
            );
    }
    {
        // When the token limit is bumped should pass
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(response, [0u8; 1000]);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };

    // ANCHOR: submit_response_script
    let submitted_tx = script_instance.main(my_struct).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;
    // ANCHOR_END: submit_response_script

    assert_eq!(value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_transaction_builder() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.try_provider()?;

    // ANCHOR: script_call_tb
    let script_call_handler = script_instance.main(1, 2);

    let mut tb = script_call_handler.transaction_builder().await?;

    // customize the builder...

    wallet.adjust_for_fee(&mut tb, 0).await?;
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(provider).await?;

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let tx_status = provider.tx_status(&tx_id).await?;

    let response = script_call_handler.get_response_from(tx_status)?;

    assert_eq!(response.value, "hello");
    // ANCHOR_END: script_call_tb

    Ok(())
}

#[tokio::test]
async fn script_encoder_config_is_applied() {
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/basic_script/out/release/basic_script-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await.expect("");
    let bin_path = "sway/scripts/basic_script/out/release/basic_script.bin";

    let script_instance_without_encoder_config = MyScript::new(wallet.clone(), bin_path);
    {
        let _encoding_ok = script_instance_without_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let script_instance_with_encoder_config =
            MyScript::new(wallet.clone(), bin_path).with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));

        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));
    }
}
#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.provider().cloned();

    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let script_instance = script_instance.with_account(no_funds_wallet);

    let value = script_instance
        .main(1000, 2000)
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value.as_ref(), "hello");

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_builder() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    // ANCHOR: preload_low_level
    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular
        .convert_to_loader()?
        .with_configurables(configurables);

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;
    // ANCHOR_END: preload_low_level

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_high_level() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let mut my_script = my_script.with_configurables(configurables);

    let arg = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    };
    let secret = my_script
        .convert_into_loader()
        .await?
        .main(arg)
        .call()
        .await?
        .value;

    assert_eq!(secret, 10001);

    Ok(())
}

#[tokio::test]
async fn high_level_blob_upload_sets_max_fee_tolerance() -> Result<()> {
    let node_config = NodeConfig {
        starting_gas_price: 1000000000,
        ..Default::default()
    };
    let mut wallet = WalletUnlocked::new_random(None);
    let coins = setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, u64::MAX);
    let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let loader = Executable::from_bytes(std::fs::read(
        "sway/scripts/script_blobs/out/release/script_blobs.bin",
    )?)
    .convert_to_loader()?;

    let zero_tolerance_fee = {
        let mut tb = BlobTransactionBuilder::default()
            .with_blob(loader.blob())
            .with_max_fee_estimation_tolerance(0.);

        wallet.adjust_for_fee(&mut tb, 0).await?;

        wallet.add_witnesses(&mut tb)?;
        let tx = tb.build(&provider).await?;
        tx.max_fee().unwrap()
    };

    let mut my_script = my_script;
    my_script.convert_into_loader().await?;

    let max_fee_of_sent_blob_tx = provider
        .get_transactions(PaginationRequest {
            cursor: None,
            results: 100,
            direction: PageDirection::Forward,
        })
        .await?
        .results
        .into_iter()
        .find_map(|tx| {
            if let TransactionType::Blob(blob_transaction) = tx.transaction {
                blob_transaction.max_fee()
            } else {
                None
            }
        })
        .unwrap();

    assert_eq!(
        max_fee_of_sent_blob_tx,
        (zero_tolerance_fee as f32 * (1.0 + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)).ceil() as u64,
        "the blob upload tx should have had the max fee increased by the default estimation tolerance"
    );

    Ok(())
}

#[tokio::test]
async fn no_data_section_blob_run() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/empty",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let mut my_script = my_script;

    // ANCHOR: preload_high_level
    my_script.convert_into_loader().await?.main().call().await?;
    // ANCHOR_END: preload_high_level

    Ok(())
}

#[tokio::test]
async fn loader_script_calling_loader_proxy() -> Result<()> {
    setup_program_test!(
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            ),
            Contract(name = "MyProxy", project = "e2e/sway/contracts/proxy"),
            Script(name = "MyScript", project = "e2e/sway/scripts/script_proxy"),
        ),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id.clone(), wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let mut my_script = my_script;
    let result = my_script
        .convert_into_loader()
        .await?
        .main(proxy_id.clone())
        .with_contract_ids(&[contract_id, proxy_id])
        .call()
        .await?;

    assert!(result.value);

    Ok(())
}

#[tokio::test]
async fn loader_can_be_presented_as_a_normal_script_with_shifted_configurables() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular.clone().convert_to_loader()?;

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let configurables: Configurables = configurables.into();

    let shifted_configurables = configurables
        .with_shifted_offsets(-(regular.data_offset_in_code().unwrap() as i64))
        .unwrap()
        .with_shifted_offsets(loader.data_offset_in_code() as i64)
        .unwrap();

    let loader_posing_as_normal_script =
        Executable::from_bytes(loader.code()).with_configurables(shifted_configurables);

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader_posing_as_normal_script.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;

    Ok(())
}\n```

The upload of the blob is handled inside of the convert_into_loader method. If you want more fine-grained control over it, you can create the script transaction manually:

```rust\nuse std::time::Duration;

use fuel_tx::Output;
use fuels::{
    client::{PageDirection, PaginationRequest},
    core::{
        codec::{DecoderConfig, EncoderConfig},
        traits::Tokenizable,
        Configurables,
    },
    prelude::*,
    programs::{executable::Executable, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE},
    types::{Bits256, Identity},
};

#[tokio::test]
async fn main_function_arguments() -> Result<()> {
    // ANCHOR: script_with_arguments
    // The abigen is used for the same purpose as with contracts (Rust bindings)
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/scripts/arguments/out/release/arguments.bin";
    let script_instance = MyScript::new(wallet, bin_path);

    let bim = Bimbam { val: 90 };
    let bam = SugarySnack {
        twix: 100,
        mars: 1000,
    };

    let result = script_instance.main(bim, bam).call().await?;

    let expected = Bimbam { val: 2190 };
    assert_eq!(result.value, expected);
    // ANCHOR_END: script_with_arguments
    Ok(())
}

#[tokio::test]
async fn script_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let a = 4u64;
    let b = 2u32;
    let estimated_gas_used = script_instance
        .main(a, b)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = script_instance.main(a, b).call().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);

    Ok(())
}

#[tokio::test]
async fn test_basic_script_with_tx_policies() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "bimbam_script",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "bimbam_script",
            wallet = "wallet"
        )
    );

    let a = 1000u64;
    let b = 2000u32;
    let result = script_instance.main(a, b).call().await?;
    assert_eq!(result.value, "hello");

    // ANCHOR: script_with_tx_policies
    let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
    let result = script_instance
        .main(a, b)
        .with_tx_policies(tx_policies)
        .call()
        .await?;
    // ANCHOR_END: script_with_tx_policies
    assert_eq!(result.value, "hello");

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "transfer_script",
            project = "e2e/sway/scripts/transfer_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "transfer_script",
            wallet = "wallet"
        )
    );

    let provider = wallet.try_provider()?.clone();
    let mut receiver = WalletUnlocked::new_random(None);
    receiver.set_provider(provider);

    let amount = 1000;
    let asset_id = AssetId::zeroed();
    let script_call = script_instance.main(
        amount,
        asset_id,
        Identity::Address(receiver.address().into()),
    );
    let inputs = wallet
        .get_asset_inputs_for_amount(asset_id, amount, None)
        .await?;
    let output = Output::change(wallet.address().into(), 0, asset_id);
    let _ = script_call
        .with_inputs(inputs)
        .with_outputs(vec![output])
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    let receiver_balance = receiver.get_asset_balance(&asset_id).await?;
    assert_eq!(receiver_balance, amount);

    Ok(())
}

#[tokio::test]
async fn test_script_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };
    let response = script_instance.main(my_struct).call().await?;

    assert_eq!(response.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_enum"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_enum = MyEnum::Two;
    let response = script_instance.main(my_enum).call().await?;

    assert_eq!(response.value, 2);
    Ok(())
}

#[tokio::test]
async fn test_script_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_array"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_array: [u64; 4] = [1, 2, 3, 4];
    let response = script_instance.main(my_array).call().await?;

    assert_eq!(response.value, 10);
    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_on_script_call() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_needs_custom_decoder"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    {
        // Will fail if max_tokens too low
        script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 101,
                ..Default::default()
            })
            .call()
            .await
            .expect_err(
                "Should fail because return type has more tokens than what is allowed by default",
            );
    }
    {
        // When the token limit is bumped should pass
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(response, [0u8; 1000]);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };

    // ANCHOR: submit_response_script
    let submitted_tx = script_instance.main(my_struct).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;
    // ANCHOR_END: submit_response_script

    assert_eq!(value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_transaction_builder() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.try_provider()?;

    // ANCHOR: script_call_tb
    let script_call_handler = script_instance.main(1, 2);

    let mut tb = script_call_handler.transaction_builder().await?;

    // customize the builder...

    wallet.adjust_for_fee(&mut tb, 0).await?;
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(provider).await?;

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let tx_status = provider.tx_status(&tx_id).await?;

    let response = script_call_handler.get_response_from(tx_status)?;

    assert_eq!(response.value, "hello");
    // ANCHOR_END: script_call_tb

    Ok(())
}

#[tokio::test]
async fn script_encoder_config_is_applied() {
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/basic_script/out/release/basic_script-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await.expect("");
    let bin_path = "sway/scripts/basic_script/out/release/basic_script.bin";

    let script_instance_without_encoder_config = MyScript::new(wallet.clone(), bin_path);
    {
        let _encoding_ok = script_instance_without_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let script_instance_with_encoder_config =
            MyScript::new(wallet.clone(), bin_path).with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));

        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));
    }
}
#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.provider().cloned();

    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let script_instance = script_instance.with_account(no_funds_wallet);

    let value = script_instance
        .main(1000, 2000)
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value.as_ref(), "hello");

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_builder() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    // ANCHOR: preload_low_level
    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular
        .convert_to_loader()?
        .with_configurables(configurables);

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;
    // ANCHOR_END: preload_low_level

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_high_level() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let mut my_script = my_script.with_configurables(configurables);

    let arg = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    };
    let secret = my_script
        .convert_into_loader()
        .await?
        .main(arg)
        .call()
        .await?
        .value;

    assert_eq!(secret, 10001);

    Ok(())
}

#[tokio::test]
async fn high_level_blob_upload_sets_max_fee_tolerance() -> Result<()> {
    let node_config = NodeConfig {
        starting_gas_price: 1000000000,
        ..Default::default()
    };
    let mut wallet = WalletUnlocked::new_random(None);
    let coins = setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, u64::MAX);
    let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let loader = Executable::from_bytes(std::fs::read(
        "sway/scripts/script_blobs/out/release/script_blobs.bin",
    )?)
    .convert_to_loader()?;

    let zero_tolerance_fee = {
        let mut tb = BlobTransactionBuilder::default()
            .with_blob(loader.blob())
            .with_max_fee_estimation_tolerance(0.);

        wallet.adjust_for_fee(&mut tb, 0).await?;

        wallet.add_witnesses(&mut tb)?;
        let tx = tb.build(&provider).await?;
        tx.max_fee().unwrap()
    };

    let mut my_script = my_script;
    my_script.convert_into_loader().await?;

    let max_fee_of_sent_blob_tx = provider
        .get_transactions(PaginationRequest {
            cursor: None,
            results: 100,
            direction: PageDirection::Forward,
        })
        .await?
        .results
        .into_iter()
        .find_map(|tx| {
            if let TransactionType::Blob(blob_transaction) = tx.transaction {
                blob_transaction.max_fee()
            } else {
                None
            }
        })
        .unwrap();

    assert_eq!(
        max_fee_of_sent_blob_tx,
        (zero_tolerance_fee as f32 * (1.0 + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)).ceil() as u64,
        "the blob upload tx should have had the max fee increased by the default estimation tolerance"
    );

    Ok(())
}

#[tokio::test]
async fn no_data_section_blob_run() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/empty",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let mut my_script = my_script;

    // ANCHOR: preload_high_level
    my_script.convert_into_loader().await?.main().call().await?;
    // ANCHOR_END: preload_high_level

    Ok(())
}

#[tokio::test]
async fn loader_script_calling_loader_proxy() -> Result<()> {
    setup_program_test!(
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            ),
            Contract(name = "MyProxy", project = "e2e/sway/contracts/proxy"),
            Script(name = "MyScript", project = "e2e/sway/scripts/script_proxy"),
        ),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id.clone(), wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let mut my_script = my_script;
    let result = my_script
        .convert_into_loader()
        .await?
        .main(proxy_id.clone())
        .with_contract_ids(&[contract_id, proxy_id])
        .call()
        .await?;

    assert!(result.value);

    Ok(())
}

#[tokio::test]
async fn loader_can_be_presented_as_a_normal_script_with_shifted_configurables() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular.clone().convert_to_loader()?;

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let configurables: Configurables = configurables.into();

    let shifted_configurables = configurables
        .with_shifted_offsets(-(regular.data_offset_in_code().unwrap() as i64))
        .unwrap()
        .with_shifted_offsets(loader.data_offset_in_code() as i64)
        .unwrap();

    let loader_posing_as_normal_script =
        Executable::from_bytes(loader.code()).with_configurables(shifted_configurables);

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader_posing_as_normal_script.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;

    Ok(())
}\n```

Predicates

You can prepare a predicate for pre-uploading without doing network requests:

```rust\nuse std::default::Default;

use fuels::{
    core::{
        codec::{ABIEncoder, EncoderConfig},
        traits::Tokenizable,
    },
    prelude::*,
    programs::executable::Executable,
    types::{coin::Coin, coin_type::CoinType, input::Input, message::Message, output::Output},
};

async fn assert_address_balance(
    address: &Bech32Address,
    provider: &Provider,
    asset_id: AssetId,
    amount: u64,
) {
    let balance = provider
        .get_asset_balance(address, asset_id)
        .await
        .expect("Could not retrieve balance");
    assert_eq!(balance, amount);
}

fn get_test_coins_and_messages(
    address: &Bech32Address,
    num_coins: u64,
    num_messages: u64,
    amount: u64,
    start_nonce: u64,
) -> (Vec<Coin>, Vec<Message>, AssetId) {
    let asset_id = AssetId::zeroed();
    let coins = setup_single_asset_coins(address, asset_id, num_coins, amount);
    let messages = (0..num_messages)
        .map(|i| {
            setup_single_message(
                &Bech32Address::default(),
                address,
                amount,
                (start_nonce + i).into(),
                vec![],
            )
        })
        .collect();

    (coins, messages, asset_id)
}

fn get_test_message_w_data(address: &Bech32Address, amount: u64, nonce: u64) -> Message {
    setup_single_message(
        &Bech32Address::default(),
        address,
        amount,
        nonce.into(),
        vec![1, 2, 3],
    )
}

// Setup function used to assign coins and messages to a predicate address
// and create a `receiver` wallet
async fn setup_predicate_test(
    predicate_address: &Bech32Address,
    num_coins: u64,
    num_messages: u64,
    amount: u64,
) -> Result<(Provider, u64, WalletUnlocked, u64, AssetId, WalletUnlocked)> {
    let receiver_num_coins = 1;
    let receiver_amount = 1;
    let receiver_balance = receiver_num_coins * receiver_amount;

    let predicate_balance = (num_coins + num_messages) * amount;
    let mut receiver = WalletUnlocked::new_random(None);
    let mut extra_wallet = WalletUnlocked::new_random(None);

    let (mut coins, messages, asset_id) =
        get_test_coins_and_messages(predicate_address, num_coins, num_messages, amount, 0);

    coins.extend(setup_single_asset_coins(
        receiver.address(),
        asset_id,
        receiver_num_coins,
        receiver_amount,
    ));
    coins.extend(setup_single_asset_coins(
        extra_wallet.address(),
        AssetId::zeroed(),
        10_000,
        10_000,
    ));

    coins.extend(setup_single_asset_coins(
        predicate_address,
        AssetId::from([1u8; 32]),
        num_coins,
        amount,
    ));

    let provider = setup_test_provider(coins, messages, None, None).await?;
    receiver.set_provider(provider.clone());
    extra_wallet.set_provider(provider.clone());

    Ok((
        provider,
        predicate_balance,
        receiver,
        receiver_balance,
        asset_id,
        extra_wallet,
    ))
}

#[tokio::test]
async fn transfer_coins_and_messages_to_predicate() -> Result<()> {
    let num_coins = 16;
    let num_messages = 32;
    let amount = 64;
    let total_balance = (num_coins + num_messages) * amount;

    let mut wallet = WalletUnlocked::new_random(None);

    let (coins, messages, asset_id) =
        get_test_coins_and_messages(wallet.address(), num_coins, num_messages, amount, 0);

    let provider = setup_test_provider(coins, messages, None, None).await?;

    wallet.set_provider(provider.clone());

    let predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    wallet
        .transfer(
            predicate.address(),
            total_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has received the funds
    assert_address_balance(
        predicate.address(),
        &provider,
        asset_id,
        total_balance - expected_fee,
    )
    .await;
    Ok(())
}

#[tokio::test]
async fn spend_predicate_coins_messages_basic() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(4097, 4097)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn pay_with_predicate() -> Result<()> {
    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/types/predicates/u64/out/release/u64-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(32768)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/types/predicates/u64/out/release/u64.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();
    let tx_policies = TxPolicies::default()
        .with_tip(1)
        .with_script_gas_limit(1_000_000);

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    let consensus_parameters = provider.consensus_parameters().await?;
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        192 - expected_fee
    );

    let response = contract_methods
        .initialize_counter(42) // Build the ABI call
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    assert_eq!(42, response.value);
    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 2;
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        191 - expected_fee
    );

    Ok(())
}

#[tokio::test]
async fn pay_with_predicate_vector_data() -> Result<()> {
    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi =
                "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(12, 30, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();
    let tx_policies = TxPolicies::default()
        .with_tip(1)
        .with_script_gas_limit(1_000_000);

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    let consensus_parameters = provider.consensus_parameters().await?;
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        192 - expected_fee
    );

    let response = contract_methods
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 2;
    assert_eq!(42, response.value);
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        191 - expected_fee
    );

    Ok(())
}

#[tokio::test]
async fn predicate_contract_transfer() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(2, 40, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 300;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_balances = provider.get_contract_balances(&contract_id).await?;
    assert!(contract_balances.is_empty());

    let amount = 300;
    predicate
        .force_transfer_to_contract(
            &contract_id,
            amount,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await?;

    let contract_balances = predicate
        .try_provider()?
        .get_contract_balances(&contract_id)
        .await?;
    assert_eq!(contract_balances.len(), 1);

    let random_asset_balance = contract_balances.get(&AssetId::zeroed()).unwrap();
    assert_eq!(*random_asset_balance, 300);

    Ok(())
}

#[tokio::test]
async fn predicate_transfer_to_base_layer() -> Result<()> {
    use std::str::FromStr;

    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(22, 20, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 300;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let amount = 1000;
    let base_layer_address =
        Address::from_str("0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe")?;
    let base_layer_address = Bech32Address::from(base_layer_address);

    let (tx_id, msg_nonce, _receipts) = predicate
        .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
        .await?;

    // Create the next commit block to be able generate the proof
    provider.produce_blocks(1, None).await?;

    let proof = predicate
        .try_provider()?
        .get_message_proof(&tx_id, &msg_nonce, None, Some(2))
        .await?;

    assert_eq!(proof.amount, amount);
    assert_eq!(proof.recipient, base_layer_address);

    Ok(())
}

#[tokio::test]
async fn predicate_transfer_with_signed_resources() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(2, 40, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let predicate_num_coins = 4;
    let predicate_num_messages = 3;
    let predicate_amount = 1000;
    let predicate_balance = (predicate_num_coins + predicate_num_messages) * predicate_amount;

    let mut wallet = WalletUnlocked::new_random(None);
    let wallet_num_coins = 4;
    let wallet_num_messages = 3;
    let wallet_amount = 1000;
    let wallet_balance = (wallet_num_coins + wallet_num_messages) * wallet_amount;

    let (mut coins, mut messages, asset_id) = get_test_coins_and_messages(
        predicate.address(),
        predicate_num_coins,
        predicate_num_messages,
        predicate_amount,
        0,
    );
    let (wallet_coins, wallet_messages, _) = get_test_coins_and_messages(
        wallet.address(),
        wallet_num_coins,
        wallet_num_messages,
        wallet_amount,
        predicate_num_messages,
    );

    coins.extend(wallet_coins);
    messages.extend(wallet_messages);

    let provider = setup_test_provider(coins, messages, None, None).await?;
    wallet.set_provider(provider.clone());
    predicate.set_provider(provider.clone());

    let mut inputs = wallet
        .get_asset_inputs_for_amount(asset_id, wallet_balance, None)
        .await?;
    let predicate_inputs = predicate
        .get_asset_inputs_for_amount(asset_id, predicate_balance, None)
        .await?;
    inputs.extend(predicate_inputs);

    let outputs = vec![Output::change(predicate.address().into(), 0, asset_id)];

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, Default::default());
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(&provider).await?;

    provider.send_transaction_and_await_commit(tx).await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    assert_address_balance(
        predicate.address(),
        &provider,
        asset_id,
        predicate_balance + wallet_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn contract_tx_and_call_params_with_predicate() -> Result<()> {
    use fuels::prelude::*;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi =
                "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(22, 20, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 1;
    let num_messages = 1;
    let amount = 1000;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "./sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;
    println!("Contract deployed @ {contract_id}");

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();

    let tx_policies = TxPolicies::default().with_tip(100);

    let call_params_amount = 100;
    let call_params = CallParameters::default()
        .with_amount(call_params_amount)
        .with_asset_id(AssetId::zeroed());

    {
        let response = contract_methods
            .get_msg_amount()
            .with_tx_policies(tx_policies)
            .call_params(call_params.clone())?
            .call()
            .await?;

        // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
        let expected_fee = 2;
        assert_eq!(
            predicate.get_asset_balance(&AssetId::zeroed()).await?,
            1800 - expected_fee
        );
    }
    {
        let custom_asset = AssetId::from([1u8; 32]);

        let response = contract_methods
            .get_msg_amount()
            .call_params(call_params)?
            .add_custom_asset(custom_asset, 100, Some(Bech32Address::default()))
            .call()
            .await?;

        assert_eq!(predicate.get_asset_balance(&custom_asset).await?, 900);
    }

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn diff_asset_predicate_payment() -> Result<()> {
    use fuels::prelude::*;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi =
                "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(28, 14, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 1;
    let num_messages = 1;
    let amount = 1_000_000_000;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "./sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();

    let call_params = CallParameters::default()
        .with_amount(1_000_000)
        .with_asset_id(AssetId::from([1u8; 32]));

    let response = contract_methods
        .get_msg_amount()
        .call_params(call_params)?
        .call()
        .await?;

    Ok(())
}

#[tokio::test]
async fn predicate_default_configurables() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_configurables/out/release/predicate_configurables-abi.json"
    ));

    let new_struct = StructWithGeneric {
        field_1: 8u8,
        field_2: 16,
    };
    let new_enum = EnumWithGeneric::VariantOne(true);

    let predicate_data = MyPredicateEncoder::default().encode_data(
        true,
        8,
        (8, true),
        [253, 254, 255],
        new_struct,
        new_enum,
    )?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_configurables/out/release/predicate_configurables.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn predicate_configurables() -> Result<()> {
    // ANCHOR: predicate_configurables
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_configurables/out/release/predicate_configurables-abi.json"
    ));

    let new_tuple = (16, false);
    let new_array = [123, 124, 125];
    let new_struct = StructWithGeneric {
        field_1: 32u8,
        field_2: 64,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyPredicateConfigurables::default()
        .with_U8(8)?
        .with_TUPLE(new_tuple)?
        .with_ARRAY(new_array)?
        .with_STRUCT(new_struct.clone())?
        .with_ENUM(new_enum.clone())?;

    let predicate_data = MyPredicateEncoder::default()
        .encode_data(true, 8u8, new_tuple, new_array, new_struct, new_enum)?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_configurables/out/release/predicate_configurables.bin",
    )?
    .with_data(predicate_data)
    .with_configurables(configurables);
    // ANCHOR_END: predicate_configurables

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn predicate_adjust_fee_persists_message_w_data() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(4097, 4097)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let amount = 1000;
    let coins = setup_single_asset_coins(predicate.address(), AssetId::zeroed(), 1, amount);
    let message = get_test_message_w_data(predicate.address(), amount, Default::default());
    let message_input = Input::resource_predicate(
        CoinType::Message(message.clone()),
        predicate.code().to_vec(),
        predicate.data().to_vec(),
    );

    let provider = setup_test_provider(coins, vec![message.clone()], None, None).await?;
    predicate.set_provider(provider.clone());

    let mut tb = ScriptTransactionBuilder::prepare_transfer(
        vec![message_input.clone()],
        vec![],
        TxPolicies::default(),
    );
    predicate.adjust_for_fee(&mut tb, 0).await?;

    let tx = tb.build(&provider).await?;

    assert_eq!(tx.inputs().len(), 2);
    assert_eq!(tx.inputs()[0].message_id().unwrap(), message.message_id());

    Ok(())
}

#[tokio::test]
async fn predicate_transfer_non_base_asset() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(32, 32)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let mut wallet = WalletUnlocked::new_random(None);

    let amount = 5;
    let non_base_asset_id = AssetId::new([1; 32]);

    // wallet has base and predicate non base asset
    let mut coins = setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, amount);
    coins.extend(setup_single_asset_coins(
        predicate.address(),
        non_base_asset_id,
        1,
        amount,
    ));

    let provider = setup_test_provider(coins, vec![], None, None).await?;
    predicate.set_provider(provider.clone());
    wallet.set_provider(provider.clone());

    let inputs = predicate
        .get_asset_inputs_for_amount(non_base_asset_id, amount, None)
        .await?;
    let consensus_parameters = provider.consensus_parameters().await?;
    let outputs = vec![
        Output::change(wallet.address().into(), 0, non_base_asset_id),
        Output::change(
            wallet.address().into(),
            0,
            *consensus_parameters.base_asset_id(),
        ),
    ];

    let mut tb = ScriptTransactionBuilder::prepare_transfer(
        inputs,
        outputs,
        TxPolicies::default().with_tip(1),
    );

    tb.add_signer(wallet.clone())?;
    wallet.adjust_for_fee(&mut tb, 0).await?;

    let tx = tb.build(&provider).await?;

    provider
        .send_transaction_and_await_commit(tx)
        .await?
        .check(None)?;

    let wallet_balance = wallet.get_asset_balance(&non_base_asset_id).await?;

    assert_eq!(wallet_balance, amount);

    Ok(())
}

#[tokio::test]
async fn predicate_can_access_manually_added_witnesses() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_witnesses/out/release/predicate_witnesses-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(0, 1)?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_witnesses/out/release/predicate_witnesses.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 0;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let amount_to_send = 12;
    let inputs = predicate
        .get_asset_inputs_for_amount(asset_id, amount_to_send, None)
        .await?;
    let outputs =
        predicate.get_asset_outputs_for_amount(receiver.address(), asset_id, amount_to_send);

    let mut tx = ScriptTransactionBuilder::prepare_transfer(
        inputs,
        outputs,
        TxPolicies::default().with_witness_limit(32),
    )
    .build(&provider)
    .await?;

    let witness = ABIEncoder::default().encode(&[64u64.into_token()])?; // u64 because this is VM memory
    let witness2 = ABIEncoder::default().encode(&[4096u64.into_token()])?;

    tx.append_witness(witness.into())?;
    tx.append_witness(witness2.into())?;

    provider.send_transaction_and_await_commit(tx).await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    // The predicate has spent the funds
    assert_address_balance(
        predicate.address(),
        &provider,
        asset_id,
        predicate_balance - amount_to_send - expected_fee,
    )
    .await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + amount_to_send,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn tx_id_not_changed_after_adding_witnesses() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_witnesses/out/release/predicate_witnesses-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(0, 1)?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_witnesses/out/release/predicate_witnesses.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 0;
    let amount = 16;
    let (provider, _predicate_balance, receiver, _receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let amount_to_send = 12;
    let inputs = predicate
        .get_asset_inputs_for_amount(asset_id, amount_to_send, None)
        .await?;
    let outputs =
        predicate.get_asset_outputs_for_amount(receiver.address(), asset_id, amount_to_send);

    let mut tx = ScriptTransactionBuilder::prepare_transfer(
        inputs,
        outputs,
        TxPolicies::default().with_witness_limit(32),
    )
    .build(&provider)
    .await?;

    let consensus_parameters = provider.consensus_parameters().await?;
    let chain_id = consensus_parameters.chain_id();
    let tx_id = tx.id(chain_id);

    let witness = ABIEncoder::default().encode(&[64u64.into_token()])?; // u64 because this is VM memory
    let witness2 = ABIEncoder::default().encode(&[4096u64.into_token()])?;

    tx.append_witness(witness.into())?;
    tx.append_witness(witness2.into())?;
    let tx_id_after_witnesses = tx.id(chain_id);

    let tx_id_from_provider = provider.send_transaction(tx).await?;

    assert_eq!(tx_id, tx_id_after_witnesses);
    assert_eq!(tx_id, tx_id_from_provider);

    Ok(())
}

#[tokio::test]
async fn predicate_encoder_config_is_applied() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));
    {
        let _encoding_ok = MyPredicateEncoder::default()
            .encode_data(4097, 4097)
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let encoding_error = MyPredicateEncoder::new(encoder_config)
            .encode_data(4097, 4097)
            .expect_err("should fail");

        assert!(encoding_error
            .to_string()
            .contains("token limit `1` reached while encoding"));
    }

    Ok(())
}

#[tokio::test]
async fn predicate_transfers_non_base_asset() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(4097, 4097)?;
    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_message = 6;
    let amount = 20;
    let (provider, _, receiver, _, _, _) =
        setup_predicate_test(predicate.address(), num_coins, num_message, amount).await?;
    predicate.set_provider(provider);
    let other_asset_id = AssetId::from([1u8; 32]);

    let send_amount = num_coins * amount;
    predicate
        .transfer(
            receiver.address(),
            send_amount,
            other_asset_id,
            TxPolicies::default(),
        )
        .await?;

    assert_eq!(predicate.get_asset_balance(&other_asset_id).await?, 0,);

    assert_eq!(
        receiver.get_asset_balance(&other_asset_id).await?,
        send_amount,
    );

    Ok(())
}

#[tokio::test]
async fn predicate_with_invalid_data_fails() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(0, 100)?;
    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_message = 6;
    let amount = 20;
    let (provider, _, receiver, _, _, _) =
        setup_predicate_test(predicate.address(), num_coins, num_message, amount).await?;
    predicate.set_provider(provider);
    let other_asset_id = AssetId::from([1u8; 32]);

    let send_amount = num_coins * amount;
    let error_string = predicate
        .transfer(
            receiver.address(),
            send_amount,
            other_asset_id,
            TxPolicies::default(),
        )
        .await
        .unwrap_err()
        .to_string();

    assert!(error_string.contains("PredicateVerificationFailed(Panic(PredicateReturnedNonOne))"));
    assert_eq!(receiver.get_asset_balance(&other_asset_id).await?, 0);

    Ok(())
}

#[tokio::test]
async fn predicate_blobs() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_blobs/out/release/predicate_blobs-abi.json"
    ));

    // ANCHOR: preparing_the_predicate
    let configurables = MyPredicateConfigurables::default().with_SECRET_NUMBER(10001)?;

    let predicate_data = MyPredicateEncoder::default().encode_data(1, 19)?;

    let executable =
        Executable::load_from("sway/predicates/predicate_blobs/out/release/predicate_blobs.bin")?;

    let loader = executable
        .convert_to_loader()?
        .with_configurables(configurables);

    let mut predicate: Predicate = Predicate::from_code(loader.code()).with_data(predicate_data);
    // ANCHOR_END: preparing_the_predicate

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, extra_wallet) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    // we don't want to pay with the recipient wallet so that we don't affect the assertion we're
    // gonna make later on
    // ANCHOR: uploading_the_blob
    loader.upload_blob(extra_wallet).await?;

    predicate.set_provider(provider.clone());

    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;
    // ANCHOR_END: uploading_the_blob

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn predicate_configurables_in_blobs() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_configurables/out/release/predicate_configurables-abi.json"
    ));

    let new_tuple = (16, false);
    let new_array = [123, 124, 125];
    let new_struct = StructWithGeneric {
        field_1: 32u8,
        field_2: 64,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyPredicateConfigurables::default()
        .with_U8(8)?
        .with_TUPLE(new_tuple)?
        .with_ARRAY(new_array)?
        .with_STRUCT(new_struct.clone())?
        .with_ENUM(new_enum.clone())?;

    let predicate_data = MyPredicateEncoder::default()
        .encode_data(true, 8u8, new_tuple, new_array, new_struct, new_enum)?;

    let executable = Executable::load_from(
        "sway/predicates/predicate_configurables/out/release/predicate_configurables.bin",
    )?;

    let loader = executable
        .convert_to_loader()?
        .with_configurables(configurables);

    let mut predicate: Predicate = Predicate::from_code(loader.code()).with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, extra_wallet) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    loader.upload_blob(extra_wallet).await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}\n```

Once you want to execute the predicate, you must beforehand upload the blob containing its code:

```rust\nuse std::default::Default;

use fuels::{
    core::{
        codec::{ABIEncoder, EncoderConfig},
        traits::Tokenizable,
    },
    prelude::*,
    programs::executable::Executable,
    types::{coin::Coin, coin_type::CoinType, input::Input, message::Message, output::Output},
};

async fn assert_address_balance(
    address: &Bech32Address,
    provider: &Provider,
    asset_id: AssetId,
    amount: u64,
) {
    let balance = provider
        .get_asset_balance(address, asset_id)
        .await
        .expect("Could not retrieve balance");
    assert_eq!(balance, amount);
}

fn get_test_coins_and_messages(
    address: &Bech32Address,
    num_coins: u64,
    num_messages: u64,
    amount: u64,
    start_nonce: u64,
) -> (Vec<Coin>, Vec<Message>, AssetId) {
    let asset_id = AssetId::zeroed();
    let coins = setup_single_asset_coins(address, asset_id, num_coins, amount);
    let messages = (0..num_messages)
        .map(|i| {
            setup_single_message(
                &Bech32Address::default(),
                address,
                amount,
                (start_nonce + i).into(),
                vec![],
            )
        })
        .collect();

    (coins, messages, asset_id)
}

fn get_test_message_w_data(address: &Bech32Address, amount: u64, nonce: u64) -> Message {
    setup_single_message(
        &Bech32Address::default(),
        address,
        amount,
        nonce.into(),
        vec![1, 2, 3],
    )
}

// Setup function used to assign coins and messages to a predicate address
// and create a `receiver` wallet
async fn setup_predicate_test(
    predicate_address: &Bech32Address,
    num_coins: u64,
    num_messages: u64,
    amount: u64,
) -> Result<(Provider, u64, WalletUnlocked, u64, AssetId, WalletUnlocked)> {
    let receiver_num_coins = 1;
    let receiver_amount = 1;
    let receiver_balance = receiver_num_coins * receiver_amount;

    let predicate_balance = (num_coins + num_messages) * amount;
    let mut receiver = WalletUnlocked::new_random(None);
    let mut extra_wallet = WalletUnlocked::new_random(None);

    let (mut coins, messages, asset_id) =
        get_test_coins_and_messages(predicate_address, num_coins, num_messages, amount, 0);

    coins.extend(setup_single_asset_coins(
        receiver.address(),
        asset_id,
        receiver_num_coins,
        receiver_amount,
    ));
    coins.extend(setup_single_asset_coins(
        extra_wallet.address(),
        AssetId::zeroed(),
        10_000,
        10_000,
    ));

    coins.extend(setup_single_asset_coins(
        predicate_address,
        AssetId::from([1u8; 32]),
        num_coins,
        amount,
    ));

    let provider = setup_test_provider(coins, messages, None, None).await?;
    receiver.set_provider(provider.clone());
    extra_wallet.set_provider(provider.clone());

    Ok((
        provider,
        predicate_balance,
        receiver,
        receiver_balance,
        asset_id,
        extra_wallet,
    ))
}

#[tokio::test]
async fn transfer_coins_and_messages_to_predicate() -> Result<()> {
    let num_coins = 16;
    let num_messages = 32;
    let amount = 64;
    let total_balance = (num_coins + num_messages) * amount;

    let mut wallet = WalletUnlocked::new_random(None);

    let (coins, messages, asset_id) =
        get_test_coins_and_messages(wallet.address(), num_coins, num_messages, amount, 0);

    let provider = setup_test_provider(coins, messages, None, None).await?;

    wallet.set_provider(provider.clone());

    let predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    wallet
        .transfer(
            predicate.address(),
            total_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has received the funds
    assert_address_balance(
        predicate.address(),
        &provider,
        asset_id,
        total_balance - expected_fee,
    )
    .await;
    Ok(())
}

#[tokio::test]
async fn spend_predicate_coins_messages_basic() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(4097, 4097)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn pay_with_predicate() -> Result<()> {
    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi = "e2e/sway/types/predicates/u64/out/release/u64-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(32768)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/types/predicates/u64/out/release/u64.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();
    let tx_policies = TxPolicies::default()
        .with_tip(1)
        .with_script_gas_limit(1_000_000);

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    let consensus_parameters = provider.consensus_parameters().await?;
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        192 - expected_fee
    );

    let response = contract_methods
        .initialize_counter(42) // Build the ABI call
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    assert_eq!(42, response.value);
    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 2;
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        191 - expected_fee
    );

    Ok(())
}

#[tokio::test]
async fn pay_with_predicate_vector_data() -> Result<()> {
    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi =
                "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(12, 30, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();
    let tx_policies = TxPolicies::default()
        .with_tip(1)
        .with_script_gas_limit(1_000_000);

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    let consensus_parameters = provider.consensus_parameters().await?;
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        192 - expected_fee
    );

    let response = contract_methods
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 2;
    assert_eq!(42, response.value);
    assert_eq!(
        predicate
            .get_asset_balance(consensus_parameters.base_asset_id())
            .await?,
        191 - expected_fee
    );

    Ok(())
}

#[tokio::test]
async fn predicate_contract_transfer() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(2, 40, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 300;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_balances = provider.get_contract_balances(&contract_id).await?;
    assert!(contract_balances.is_empty());

    let amount = 300;
    predicate
        .force_transfer_to_contract(
            &contract_id,
            amount,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await?;

    let contract_balances = predicate
        .try_provider()?
        .get_contract_balances(&contract_id)
        .await?;
    assert_eq!(contract_balances.len(), 1);

    let random_asset_balance = contract_balances.get(&AssetId::zeroed()).unwrap();
    assert_eq!(*random_asset_balance, 300);

    Ok(())
}

#[tokio::test]
async fn predicate_transfer_to_base_layer() -> Result<()> {
    use std::str::FromStr;

    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(22, 20, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 300;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let amount = 1000;
    let base_layer_address =
        Address::from_str("0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe")?;
    let base_layer_address = Bech32Address::from(base_layer_address);

    let (tx_id, msg_nonce, _receipts) = predicate
        .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
        .await?;

    // Create the next commit block to be able generate the proof
    provider.produce_blocks(1, None).await?;

    let proof = predicate
        .try_provider()?
        .get_message_proof(&tx_id, &msg_nonce, None, Some(2))
        .await?;

    assert_eq!(proof.amount, amount);
    assert_eq!(proof.recipient, base_layer_address);

    Ok(())
}

#[tokio::test]
async fn predicate_transfer_with_signed_resources() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(2, 40, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let predicate_num_coins = 4;
    let predicate_num_messages = 3;
    let predicate_amount = 1000;
    let predicate_balance = (predicate_num_coins + predicate_num_messages) * predicate_amount;

    let mut wallet = WalletUnlocked::new_random(None);
    let wallet_num_coins = 4;
    let wallet_num_messages = 3;
    let wallet_amount = 1000;
    let wallet_balance = (wallet_num_coins + wallet_num_messages) * wallet_amount;

    let (mut coins, mut messages, asset_id) = get_test_coins_and_messages(
        predicate.address(),
        predicate_num_coins,
        predicate_num_messages,
        predicate_amount,
        0,
    );
    let (wallet_coins, wallet_messages, _) = get_test_coins_and_messages(
        wallet.address(),
        wallet_num_coins,
        wallet_num_messages,
        wallet_amount,
        predicate_num_messages,
    );

    coins.extend(wallet_coins);
    messages.extend(wallet_messages);

    let provider = setup_test_provider(coins, messages, None, None).await?;
    wallet.set_provider(provider.clone());
    predicate.set_provider(provider.clone());

    let mut inputs = wallet
        .get_asset_inputs_for_amount(asset_id, wallet_balance, None)
        .await?;
    let predicate_inputs = predicate
        .get_asset_inputs_for_amount(asset_id, predicate_balance, None)
        .await?;
    inputs.extend(predicate_inputs);

    let outputs = vec![Output::change(predicate.address().into(), 0, asset_id)];

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, Default::default());
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(&provider).await?;

    provider.send_transaction_and_await_commit(tx).await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    assert_address_balance(
        predicate.address(),
        &provider,
        asset_id,
        predicate_balance + wallet_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn contract_tx_and_call_params_with_predicate() -> Result<()> {
    use fuels::prelude::*;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi =
                "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(22, 20, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 1;
    let num_messages = 1;
    let amount = 1000;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "./sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;
    println!("Contract deployed @ {contract_id}");

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();

    let tx_policies = TxPolicies::default().with_tip(100);

    let call_params_amount = 100;
    let call_params = CallParameters::default()
        .with_amount(call_params_amount)
        .with_asset_id(AssetId::zeroed());

    {
        let response = contract_methods
            .get_msg_amount()
            .with_tx_policies(tx_policies)
            .call_params(call_params.clone())?
            .call()
            .await?;

        // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
        let expected_fee = 2;
        assert_eq!(
            predicate.get_asset_balance(&AssetId::zeroed()).await?,
            1800 - expected_fee
        );
    }
    {
        let custom_asset = AssetId::from([1u8; 32]);

        let response = contract_methods
            .get_msg_amount()
            .call_params(call_params)?
            .add_custom_asset(custom_asset, 100, Some(Bech32Address::default()))
            .call()
            .await?;

        assert_eq!(predicate.get_asset_balance(&custom_asset).await?, 900);
    }

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn diff_asset_predicate_payment() -> Result<()> {
    use fuels::prelude::*;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ),
        Predicate(
            name = "MyPredicate",
            abi =
                "e2e/sway/types/predicates/predicate_vector/out/release/predicate_vector-abi.json"
        )
    );

    let predicate_data = MyPredicateEncoder::default().encode_data(28, 14, vec![2, 4, 42])?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 1;
    let num_messages = 1;
    let amount = 1_000_000_000;
    let (provider, _predicate_balance, _receiver, _receiver_balance, _asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "./sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id.clone(), predicate.clone()).methods();

    let call_params = CallParameters::default()
        .with_amount(1_000_000)
        .with_asset_id(AssetId::from([1u8; 32]));

    let response = contract_methods
        .get_msg_amount()
        .call_params(call_params)?
        .call()
        .await?;

    Ok(())
}

#[tokio::test]
async fn predicate_default_configurables() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_configurables/out/release/predicate_configurables-abi.json"
    ));

    let new_struct = StructWithGeneric {
        field_1: 8u8,
        field_2: 16,
    };
    let new_enum = EnumWithGeneric::VariantOne(true);

    let predicate_data = MyPredicateEncoder::default().encode_data(
        true,
        8,
        (8, true),
        [253, 254, 255],
        new_struct,
        new_enum,
    )?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_configurables/out/release/predicate_configurables.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn predicate_configurables() -> Result<()> {
    // ANCHOR: predicate_configurables
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_configurables/out/release/predicate_configurables-abi.json"
    ));

    let new_tuple = (16, false);
    let new_array = [123, 124, 125];
    let new_struct = StructWithGeneric {
        field_1: 32u8,
        field_2: 64,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyPredicateConfigurables::default()
        .with_U8(8)?
        .with_TUPLE(new_tuple)?
        .with_ARRAY(new_array)?
        .with_STRUCT(new_struct.clone())?
        .with_ENUM(new_enum.clone())?;

    let predicate_data = MyPredicateEncoder::default()
        .encode_data(true, 8u8, new_tuple, new_array, new_struct, new_enum)?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_configurables/out/release/predicate_configurables.bin",
    )?
    .with_data(predicate_data)
    .with_configurables(configurables);
    // ANCHOR_END: predicate_configurables

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn predicate_adjust_fee_persists_message_w_data() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(4097, 4097)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let amount = 1000;
    let coins = setup_single_asset_coins(predicate.address(), AssetId::zeroed(), 1, amount);
    let message = get_test_message_w_data(predicate.address(), amount, Default::default());
    let message_input = Input::resource_predicate(
        CoinType::Message(message.clone()),
        predicate.code().to_vec(),
        predicate.data().to_vec(),
    );

    let provider = setup_test_provider(coins, vec![message.clone()], None, None).await?;
    predicate.set_provider(provider.clone());

    let mut tb = ScriptTransactionBuilder::prepare_transfer(
        vec![message_input.clone()],
        vec![],
        TxPolicies::default(),
    );
    predicate.adjust_for_fee(&mut tb, 0).await?;

    let tx = tb.build(&provider).await?;

    assert_eq!(tx.inputs().len(), 2);
    assert_eq!(tx.inputs()[0].message_id().unwrap(), message.message_id());

    Ok(())
}

#[tokio::test]
async fn predicate_transfer_non_base_asset() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(32, 32)?;

    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let mut wallet = WalletUnlocked::new_random(None);

    let amount = 5;
    let non_base_asset_id = AssetId::new([1; 32]);

    // wallet has base and predicate non base asset
    let mut coins = setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, amount);
    coins.extend(setup_single_asset_coins(
        predicate.address(),
        non_base_asset_id,
        1,
        amount,
    ));

    let provider = setup_test_provider(coins, vec![], None, None).await?;
    predicate.set_provider(provider.clone());
    wallet.set_provider(provider.clone());

    let inputs = predicate
        .get_asset_inputs_for_amount(non_base_asset_id, amount, None)
        .await?;
    let consensus_parameters = provider.consensus_parameters().await?;
    let outputs = vec![
        Output::change(wallet.address().into(), 0, non_base_asset_id),
        Output::change(
            wallet.address().into(),
            0,
            *consensus_parameters.base_asset_id(),
        ),
    ];

    let mut tb = ScriptTransactionBuilder::prepare_transfer(
        inputs,
        outputs,
        TxPolicies::default().with_tip(1),
    );

    tb.add_signer(wallet.clone())?;
    wallet.adjust_for_fee(&mut tb, 0).await?;

    let tx = tb.build(&provider).await?;

    provider
        .send_transaction_and_await_commit(tx)
        .await?
        .check(None)?;

    let wallet_balance = wallet.get_asset_balance(&non_base_asset_id).await?;

    assert_eq!(wallet_balance, amount);

    Ok(())
}

#[tokio::test]
async fn predicate_can_access_manually_added_witnesses() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_witnesses/out/release/predicate_witnesses-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(0, 1)?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_witnesses/out/release/predicate_witnesses.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 0;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let amount_to_send = 12;
    let inputs = predicate
        .get_asset_inputs_for_amount(asset_id, amount_to_send, None)
        .await?;
    let outputs =
        predicate.get_asset_outputs_for_amount(receiver.address(), asset_id, amount_to_send);

    let mut tx = ScriptTransactionBuilder::prepare_transfer(
        inputs,
        outputs,
        TxPolicies::default().with_witness_limit(32),
    )
    .build(&provider)
    .await?;

    let witness = ABIEncoder::default().encode(&[64u64.into_token()])?; // u64 because this is VM memory
    let witness2 = ABIEncoder::default().encode(&[4096u64.into_token()])?;

    tx.append_witness(witness.into())?;
    tx.append_witness(witness2.into())?;

    provider.send_transaction_and_await_commit(tx).await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    // The predicate has spent the funds
    assert_address_balance(
        predicate.address(),
        &provider,
        asset_id,
        predicate_balance - amount_to_send - expected_fee,
    )
    .await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + amount_to_send,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn tx_id_not_changed_after_adding_witnesses() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_witnesses/out/release/predicate_witnesses-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(0, 1)?;

    let mut predicate: Predicate = Predicate::load_from(
        "sway/predicates/predicate_witnesses/out/release/predicate_witnesses.bin",
    )?
    .with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 0;
    let amount = 16;
    let (provider, _predicate_balance, receiver, _receiver_balance, asset_id, _) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    let amount_to_send = 12;
    let inputs = predicate
        .get_asset_inputs_for_amount(asset_id, amount_to_send, None)
        .await?;
    let outputs =
        predicate.get_asset_outputs_for_amount(receiver.address(), asset_id, amount_to_send);

    let mut tx = ScriptTransactionBuilder::prepare_transfer(
        inputs,
        outputs,
        TxPolicies::default().with_witness_limit(32),
    )
    .build(&provider)
    .await?;

    let consensus_parameters = provider.consensus_parameters().await?;
    let chain_id = consensus_parameters.chain_id();
    let tx_id = tx.id(chain_id);

    let witness = ABIEncoder::default().encode(&[64u64.into_token()])?; // u64 because this is VM memory
    let witness2 = ABIEncoder::default().encode(&[4096u64.into_token()])?;

    tx.append_witness(witness.into())?;
    tx.append_witness(witness2.into())?;
    let tx_id_after_witnesses = tx.id(chain_id);

    let tx_id_from_provider = provider.send_transaction(tx).await?;

    assert_eq!(tx_id, tx_id_after_witnesses);
    assert_eq!(tx_id, tx_id_from_provider);

    Ok(())
}

#[tokio::test]
async fn predicate_encoder_config_is_applied() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));
    {
        let _encoding_ok = MyPredicateEncoder::default()
            .encode_data(4097, 4097)
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let encoding_error = MyPredicateEncoder::new(encoder_config)
            .encode_data(4097, 4097)
            .expect_err("should fail");

        assert!(encoding_error
            .to_string()
            .contains("token limit `1` reached while encoding"));
    }

    Ok(())
}

#[tokio::test]
async fn predicate_transfers_non_base_asset() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(4097, 4097)?;
    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_message = 6;
    let amount = 20;
    let (provider, _, receiver, _, _, _) =
        setup_predicate_test(predicate.address(), num_coins, num_message, amount).await?;
    predicate.set_provider(provider);
    let other_asset_id = AssetId::from([1u8; 32]);

    let send_amount = num_coins * amount;
    predicate
        .transfer(
            receiver.address(),
            send_amount,
            other_asset_id,
            TxPolicies::default(),
        )
        .await?;

    assert_eq!(predicate.get_asset_balance(&other_asset_id).await?, 0,);

    assert_eq!(
        receiver.get_asset_balance(&other_asset_id).await?,
        send_amount,
    );

    Ok(())
}

#[tokio::test]
async fn predicate_with_invalid_data_fails() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ));

    let predicate_data = MyPredicateEncoder::default().encode_data(0, 100)?;
    let mut predicate: Predicate =
        Predicate::load_from("sway/predicates/basic_predicate/out/release/basic_predicate.bin")?
            .with_data(predicate_data);

    let num_coins = 4;
    let num_message = 6;
    let amount = 20;
    let (provider, _, receiver, _, _, _) =
        setup_predicate_test(predicate.address(), num_coins, num_message, amount).await?;
    predicate.set_provider(provider);
    let other_asset_id = AssetId::from([1u8; 32]);

    let send_amount = num_coins * amount;
    let error_string = predicate
        .transfer(
            receiver.address(),
            send_amount,
            other_asset_id,
            TxPolicies::default(),
        )
        .await
        .unwrap_err()
        .to_string();

    assert!(error_string.contains("PredicateVerificationFailed(Panic(PredicateReturnedNonOne))"));
    assert_eq!(receiver.get_asset_balance(&other_asset_id).await?, 0);

    Ok(())
}

#[tokio::test]
async fn predicate_blobs() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_blobs/out/release/predicate_blobs-abi.json"
    ));

    // ANCHOR: preparing_the_predicate
    let configurables = MyPredicateConfigurables::default().with_SECRET_NUMBER(10001)?;

    let predicate_data = MyPredicateEncoder::default().encode_data(1, 19)?;

    let executable =
        Executable::load_from("sway/predicates/predicate_blobs/out/release/predicate_blobs.bin")?;

    let loader = executable
        .convert_to_loader()?
        .with_configurables(configurables);

    let mut predicate: Predicate = Predicate::from_code(loader.code()).with_data(predicate_data);
    // ANCHOR_END: preparing_the_predicate

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, extra_wallet) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    // we don't want to pay with the recipient wallet so that we don't affect the assertion we're
    // gonna make later on
    // ANCHOR: uploading_the_blob
    loader.upload_blob(extra_wallet).await?;

    predicate.set_provider(provider.clone());

    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;
    // ANCHOR_END: uploading_the_blob

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}

#[tokio::test]
async fn predicate_configurables_in_blobs() -> Result<()> {
    abigen!(Predicate(
        name = "MyPredicate",
        abi = "e2e/sway/predicates/predicate_configurables/out/release/predicate_configurables-abi.json"
    ));

    let new_tuple = (16, false);
    let new_array = [123, 124, 125];
    let new_struct = StructWithGeneric {
        field_1: 32u8,
        field_2: 64,
    };
    let new_enum = EnumWithGeneric::VariantTwo;

    let configurables = MyPredicateConfigurables::default()
        .with_U8(8)?
        .with_TUPLE(new_tuple)?
        .with_ARRAY(new_array)?
        .with_STRUCT(new_struct.clone())?
        .with_ENUM(new_enum.clone())?;

    let predicate_data = MyPredicateEncoder::default()
        .encode_data(true, 8u8, new_tuple, new_array, new_struct, new_enum)?;

    let executable = Executable::load_from(
        "sway/predicates/predicate_configurables/out/release/predicate_configurables.bin",
    )?;

    let loader = executable
        .convert_to_loader()?
        .with_configurables(configurables);

    let mut predicate: Predicate = Predicate::from_code(loader.code()).with_data(predicate_data);

    let num_coins = 4;
    let num_messages = 8;
    let amount = 16;
    let (provider, predicate_balance, receiver, receiver_balance, asset_id, extra_wallet) =
        setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?;

    predicate.set_provider(provider.clone());

    loader.upload_blob(extra_wallet).await?;

    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 1;
    predicate
        .transfer(
            receiver.address(),
            predicate_balance - expected_fee,
            asset_id,
            TxPolicies::default(),
        )
        .await?;

    // The predicate has spent the funds
    assert_address_balance(predicate.address(), &provider, asset_id, 0).await;

    // Funds were transferred
    assert_address_balance(
        receiver.address(),
        &provider,
        asset_id,
        receiver_balance + predicate_balance - expected_fee,
    )
    .await;

    Ok(())
}\n```

By pre-uploading the predicate code, you allow for cheaper calls to the predicate from subsequent callers.

Custom transactions

Until now, we have used helpers to create transactions, send them with a provider, and parse the results. However, sometimes we must make custom transactions with specific inputs, outputs, witnesses, etc. In the next chapter, we will show how to use the Rust SDKs transaction builders to accomplish this.

Transaction Builders

The Rust SDK simplifies the creation of Create and Script transactions through two handy builder structs CreateTransactionBuilder, ScriptTransactionBuilder, and the TransactionBuilder trait.

Calling build(&provider) on a builder will result in the corresponding CreateTransaction or ScriptTransaction that can be submitted to the network.

Role of the transaction builders

Note This section contains additional information about the inner workings of the builders. If you are just interested in how to use them, you can skip to the next section.

The builders take on the heavy lifting behind the scenes, offering two standout advantages: handling predicate data offsets and managing witness indexing.

When your transaction involves predicates with dynamic data as inputs, like vectors, the dynamic data contains a pointer pointing to the beginning of the raw data. This pointer's validity hinges on the order of transaction inputs, and any shifting could render it invalid. However, the transaction builders conveniently postpone the resolution of these pointers until you finalize the build process.

Similarly, adding signatures for signed coins requires the signed coin input to hold an index corresponding to the signature in the witnesses array. These indexes can also become invalid if the witness order changes. The Rust SDK again defers the resolution of these indexes until the transaction is finalized. It handles the assignment of correct index witnesses behind the scenes, sparing you the hassle of dealing with indexing intricacies during input definition.

Another added benefit of the builder pattern is that it guards against changes once the transaction is finalized. The transactions resulting from a builder don't permit any changes to the struct that could cause the transaction ID to be modified. This eliminates the headache of calculating and storing a transaction ID for future use, only to accidentally modify the transaction later, resulting in a different transaction ID.

Creating a custom transaction

Here is an example outlining some of the features of the transaction builders.

In this scenario, we have a predicate that holds some bridged asset with ID bridged_asset_id. It releases it's locked assets if the transaction sends ask_amount of the base asset to the receiver address:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Our goal is to create a transaction that will use our hot wallet to transfer the ask_amount to the receiver and then send the unlocked predicate assets to a second wallet that acts as our cold storage.

Let's start by instantiating a builder. Since we don't plan to deploy a contract, the ScriptTransactionBuilder is the appropriate choice:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Next, we need to define transaction inputs of the base asset that sum up to ask_amount. We also need transaction outputs that will assign those assets to the predicate address and thereby unlock it. The methods get_asset_inputs_for_amount and get_asset_outputs_for_amount can help with that. We need to specify the asset ID, the target amount, and the target address:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Let's repeat the same process but this time for transferring the assets held by the predicate to our cold storage:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

We combine all of the inputs and outputs and set them on the builder:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

As we have used coins that require a signature, we have to add the signer to the transaction builder with:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Note The signature is not created until the transaction is finalized with build(&provider)

We need to do one more thing before we stop thinking about transaction inputs. Executing the transaction also incurs a fee that is paid with the base asset. Our base asset inputs need to be large enough so that the total amount covers the transaction fee and any other operations we are doing. The ViewOnlyAccount trait lets us use adjust_for_fee() for adjusting the transaction inputs if needed to cover the fee. The second argument to adjust_for_fee() is the total amount of the base asset that we expect our transaction to spend regardless of fees. In our case, this is the ask_amount we are transferring to the predicate.

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Note It is recommended to add signers before calling adjust_for_fee() as the estimation will include the size of the witnesses.

We can also define transaction policies. For example, we can limit the gas price by doing the following:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Our builder needs a signature from the hot wallet to unlock its coins before we call build() and submit the resulting transaction through the provider:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Finally, we verify the transaction succeeded and that the cold storage indeed holds the bridged asset now:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Building a transaction without signatures

If you need to build the transaction without signatures, which is useful when estimating transaction costs or simulations, you can change the build strategy used:

```rust\nuse std::time::Duration;

use fuel_tx::{
    consensus_parameters::{ConsensusParametersV1, FeeParametersV1},
    ConsensusParameters, FeeParameters, Output,
};
use fuels::{
    core::codec::{calldata, encode_fn_selector, DecoderConfig, EncoderConfig},
    prelude::*,
    programs::DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
    tx::ContractParameters,
    types::{errors::transaction::Reason, input::Input, Bits256, Identity},
};
use tokio::time::Instant;

#[tokio::test]
async fn test_multiple_args() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Make sure we can call the contract with multiple arguments
    let contract_methods = contract_instance.methods();
    let response = contract_methods.get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    let t = MyType { x: 5, y: 6 };
    let response = contract_methods.get_alt(t.clone()).call().await?;
    assert_eq!(response.value, t);

    let response = contract_methods.get_single(5).call().await?;
    assert_eq!(response.value, 5);
    Ok(())
}

#[tokio::test]
async fn test_contract_calling_contract() -> Result<()> {
    // Tests a contract call that calls another contract (FooCaller calls FooContract underneath)
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "lib_contract_instance2",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();
    let lib_contract_id2 = lib_contract_instance2.contract_id();

    // Call the contract directly. It increments the given value.
    let response = lib_contract_instance.methods().increment(42).call().await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance
        .methods()
        .increment_from_contracts(lib_contract_id, lib_contract_id2, 42)
        // Note that the two lib_contract_instances have different types
        .with_contracts(&[&lib_contract_instance, &lib_contract_instance2])
        .call()
        .await?;

    assert_eq!(86, response.value);

    // ANCHOR: external_contract
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    assert_eq!(43, response.value);

    // ANCHOR: external_contract_ids
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contract_ids(&[lib_contract_id.clone()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    assert_eq!(43, response.value);
    Ok(())
}

#[tokio::test]
async fn test_reverting_transaction() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertContract",
            project = "e2e/sway/contracts/revert_transaction_error"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .make_transaction_fail(true)
        .call()
        .await;

    assert!(matches!(
        response,
        Err(Error::Transaction(Reason::Reverted { revert_id, .. })) if revert_id == 128
    ));

    Ok(())
}

#[tokio::test]
async fn test_multiple_read_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MultiReadContract",
            project = "e2e/sway/contracts/multiple_read_calls"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MultiReadContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    contract_methods.store(42).call().await?;

    // Use "simulate" because the methods don't actually
    // run a transaction, but just a dry-run
    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);

    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_multi_call_beginner() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let (val_1, val_2): (u64, u64) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_pro() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let my_type_1 = MyType { x: 1, y: 2 };
    let my_type_2 = MyType { x: 3, y: 4 };

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(5);
    let call_handler_2 = contract_methods.get_single(6);
    let call_handler_3 = contract_methods.get_alt(my_type_1.clone());
    let call_handler_4 = contract_methods.get_alt(my_type_2.clone());
    let call_handler_5 = contract_methods.get_array([7; 2]);
    let call_handler_6 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2)
        .add_call(call_handler_3)
        .add_call(call_handler_4)
        .add_call(call_handler_5)
        .add_call(call_handler_6);

    let (val_1, val_2, type_1, type_2, array_1, array_2): (
        u64,
        u64,
        MyType,
        MyType,
        [u64; 2],
        [u64; 2],
    ) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 5);
    assert_eq!(val_2, 6);
    assert_eq!(type_1, my_type_1);
    assert_eq!(type_2, my_type_2);
    assert_eq!(array_1, [7; 2]);
    assert_eq!(array_2, [42; 2]);

    Ok(())
}

#[tokio::test]
async fn test_contract_call_fee_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let gas_limit = 800;
    let tolerance = Some(0.2);
    let block_horizon = Some(1);
    let expected_gas_used = 960;
    let expected_metered_bytes_size = 824;

    let estimated_transaction_cost = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit))
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?;

    assert_eq!(estimated_transaction_cost.gas_used, expected_gas_used);
    assert_eq!(
        estimated_transaction_cost.metered_bytes_size,
        expected_metered_bytes_size
    );

    Ok(())
}

#[tokio::test]
async fn contract_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let estimated_gas_used = contract_methods
        .initialize_counter(42)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn mult_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.initialize_counter(42);
    let call_handler_2 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let tolerance = Some(0.0);
    let block_horizon = Some(1);
    let estimated_gas_used = multi_call_handler
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = multi_call_handler.call::<(u64, [u64; 2])>().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn contract_method_call_respects_maturity() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BlockHeightContract",
            project = "e2e/sway/contracts/transaction_block_height"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BlockHeightContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_w_maturity = |maturity| {
        contract_instance
            .methods()
            .calling_this_will_produce_a_block()
            .with_tx_policies(TxPolicies::default().with_maturity(maturity))
    };

    call_w_maturity(1).call().await.expect(
        "should have passed since we're calling with a maturity \
         that is less or equal to the current block height",
    );

    call_w_maturity(3).call().await.expect_err(
        "should have failed since we're calling with a maturity \
         that is greater than the current block height",
    );

    Ok(())
}

#[tokio::test]
async fn test_auth_msg_sender_from_sdk() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "AuthContract",
            project = "e2e/sway/contracts/auth_testing_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "AuthContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Contract returns true if `msg_sender()` matches `wallet.address()`.
    let response = contract_instance
        .methods()
        .check_msg_sender(wallet.address())
        .call()
        .await?;

    assert!(response.value);
    Ok(())
}

#[tokio::test]
async fn test_large_return_data() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/large_return_data"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let res = contract_methods.get_id().call().await?;

    assert_eq!(
        res.value.0,
        [
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ]
    );

    // One word-sized string
    let res = contract_methods.get_small_string().call().await?;
    assert_eq!(res.value, "gggggggg");

    // Two word-sized string
    let res = contract_methods.get_large_string().call().await?;
    assert_eq!(res.value, "ggggggggg");

    // Large struct will be bigger than a `WORD`.
    let res = contract_methods.get_large_struct().call().await?;
    assert_eq!(res.value.foo, 12);
    assert_eq!(res.value.bar, 42);

    // Array will be returned in `ReturnData`.
    let res = contract_methods.get_large_array().call().await?;
    assert_eq!(res.value, [1, 2]);

    let res = contract_methods.get_contract_id().call().await?;

    // First `value` is from `CallResponse`.
    // Second `value` is from the `ContractId` type.
    assert_eq!(
        res.value,
        ContractId::from([
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ])
    );
    Ok(())
}

#[tokio::test]
async fn can_handle_function_called_new() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().new().call().await?.value;

    assert_eq!(response, 12345);
    Ok(())
}

#[tokio::test]
async fn test_contract_setup_macro_deploy_with_salt() -> Result<()> {
    // ANCHOR: contract_setup_macro_multi
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_caller_id = contract_caller_instance.contract_id();

    let contract_caller_id2 = contract_caller_instance2.contract_id();

    // Because we deploy with salt, we can deploy the same contract multiple times
    assert_ne!(contract_caller_id, contract_caller_id2);

    // The first contract can be called because they were deployed on the same provider
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance2
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);
    // ANCHOR_END: contract_setup_macro_multi

    Ok(())
}

#[tokio::test]
async fn test_wallet_getter() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(contract_instance.account().address(), wallet.address());
    //`contract_id()` is tested in
    // async fn test_contract_calling_contract() -> Result<()> {
    Ok(())
}

#[tokio::test]
async fn test_connect_wallet() -> Result<()> {
    // ANCHOR: contract_setup_macro_manual_wallet
    let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));

    let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
    let wallet = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    // ANCHOR_END: contract_setup_macro_manual_wallet

    // pay for call with wallet
    let tx_policies = TxPolicies::default()
        .with_tip(100)
        .with_script_gas_limit(1_000_000);

    contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm that funds have been deducted
    let wallet_balance = wallet.get_asset_balance(&Default::default()).await?;
    assert!(DEFAULT_COIN_AMOUNT > wallet_balance);

    // pay for call with wallet_2
    contract_instance
        .with_account(wallet_2.clone())
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm there are no changes to wallet, wallet_2 has been charged
    let wallet_balance_second_call = wallet.get_asset_balance(&Default::default()).await?;
    let wallet_2_balance = wallet_2.get_asset_balance(&Default::default()).await?;
    assert_eq!(wallet_balance_second_call, wallet_balance);
    assert!(DEFAULT_COIN_AMOUNT > wallet_2_balance);

    Ok(())
}

async fn setup_output_variable_estimation_test() -> Result<(
    Vec<WalletUnlocked>,
    [Identity; 3],
    AssetId,
    Bech32ContractId,
)> {
    let wallet_config = WalletsConfig::new(Some(3), None, None);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;

    let contract_id = Contract::load_from(
        "sway/contracts/token_ops/out/release/token_ops.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallets[0], TxPolicies::default())
    .await?;

    let mint_asset_id = contract_id.asset_id(&Bits256::zeroed());
    let addresses = wallets
        .iter()
        .map(|wallet| wallet.address().into())
        .collect::<Vec<_>>()
        .try_into()
        .unwrap();

    Ok((wallets, addresses, mint_asset_id, contract_id))
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());
    let contract_methods = contract_instance.methods();
    let amount = 1000;

    {
        // Should fail due to lack of output variables
        let response = contract_methods
            .mint_to_addresses(amount, addresses)
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    {
        // Should add 3 output variables automatically
        let _ = contract_methods
            .mint_to_addresses(amount, addresses)
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .call()
            .await?;

        for wallet in wallets.iter() {
            let balance = wallet.get_asset_balance(&mint_asset_id).await?;
            assert_eq!(balance, amount);
        }
    }

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallets[0].clone());
    let contract_methods = contract_instance.methods();
    const NUM_OF_CALLS: u64 = 3;
    let amount = 1000;
    let total_amount = amount * NUM_OF_CALLS;

    let mut multi_call_handler = CallHandler::new_multi_call(wallets[0].clone());
    for _ in 0..NUM_OF_CALLS {
        let call_handler = contract_methods.mint_to_addresses(amount, addresses);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    wallets[0]
        .force_transfer_to_contract(
            &contract_id,
            total_amount,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await
        .unwrap();

    let base_layer_address = Bits256([1u8; 32]);
    let call_handler = contract_methods.send_message(base_layer_address, amount);
    multi_call_handler = multi_call_handler.add_call(call_handler);

    let _ = multi_call_handler
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call::<((), (), ())>()
        .await?;

    for wallet in wallets.iter() {
        let balance = wallet.get_asset_balance(&mint_asset_id).await?;
        assert_eq!(balance, 3 * amount);
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_instance_get_balances() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let (coins, asset_ids) = setup_multiple_assets_coins(wallet.address(), 2, 4, 8);

    let random_asset_id = &asset_ids[1];
    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_id = contract_instance.contract_id();

    // Check the current balance of the contract with id 'contract_id'
    let contract_balances = contract_instance.get_balances().await?;
    assert!(contract_balances.is_empty());

    // Transfer an amount to the contract
    let amount = 8;
    wallet
        .force_transfer_to_contract(contract_id, amount, *random_asset_id, TxPolicies::default())
        .await?;

    // Check that the contract now has 1 coin
    let contract_balances = contract_instance.get_balances().await?;
    assert_eq!(contract_balances.len(), 1);

    let random_asset_balance = contract_balances.get(random_asset_id).unwrap();
    assert_eq!(*random_asset_balance, amount);

    Ok(())
}

#[tokio::test]
async fn contract_call_futures_implement_send() -> Result<()> {
    use std::future::Future;

    fn tokio_spawn_imitation<T>(_: T)
    where
        T: Future + Send + 'static,
    {
    }

    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    tokio_spawn_imitation(async move {
        contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await
            .unwrap();
    });
    Ok(())
}

#[tokio::test]
async fn test_contract_set_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let res = lib_contract_instance.methods().increment(42).call().await?;
    assert_eq!(43, res.value);

    {
        // Should fail due to missing external contracts
        let res = contract_caller_instance
            .methods()
            .increment_from_contract(lib_contract_id, 42)
            .call()
            .await;

        assert!(matches!(
            res,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    let res = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    assert_eq!(43, res.value);
    Ok(())
}

#[tokio::test]
async fn test_output_variable_contract_id_estimation_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_test_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_methods = contract_caller_instance.methods();

    let mut multi_call_handler =
        CallHandler::new_multi_call(wallet.clone()).with_tx_policies(Default::default());

    for _ in 0..3 {
        let call_handler = contract_methods.increment_from_contract(lib_contract_id, 42);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    // add call that does not need ContractId
    let contract_methods = contract_test_instance.methods();
    let call_handler = contract_methods.get(5, 6);

    multi_call_handler = multi_call_handler.add_call(call_handler);

    let call_response = multi_call_handler
        .determine_missing_contracts(None)
        .await?
        .call::<(u64, u64, u64, u64)>()
        .await?;

    assert_eq!(call_response.value, (43, 43, 43, 11));

    Ok(())
}

#[tokio::test]
async fn test_contract_call_with_non_default_max_input() -> Result<()> {
    use fuels::{
        tx::{ConsensusParameters, TxParameters},
        types::coin::Coin,
    };

    let mut consensus_parameters = ConsensusParameters::default();
    let tx_params = TxParameters::default()
        .with_max_inputs(123)
        .with_max_size(1_000_000);
    consensus_parameters.set_tx_params(tx_params);
    let contract_params = ContractParameters::default().with_contract_max_size(1_000_000);
    consensus_parameters.set_contract_params(contract_params);

    let mut wallet = WalletUnlocked::new_random(None);

    let coins: Vec<Coin> = setup_single_asset_coins(
        wallet.address(),
        Default::default(),
        DEFAULT_NUM_COINS,
        DEFAULT_COIN_AMOUNT,
    );
    let chain_config = ChainConfig {
        consensus_parameters: consensus_parameters.clone(),
        ..ChainConfig::default()
    };

    let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
    wallet.set_provider(provider.clone());
    assert_eq!(consensus_parameters, provider.consensus_parameters().await?);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn test_add_custom_assets() -> Result<()> {
    let initial_amount = 100_000;
    let asset_base = AssetConfig {
        id: AssetId::zeroed(),
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_1 = AssetId::from([3u8; 32]);
    let asset_1 = AssetConfig {
        id: asset_id_1,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_2 = AssetId::from([1u8; 32]);
    let asset_2 = AssetConfig {
        id: asset_id_2,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let assets = vec![asset_base, asset_1, asset_2];

    let num_wallets = 2;
    let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
    let mut wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
    let wallet_1 = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet_1",
            random_salt = false,
        ),
    );

    let amount_1 = 5000;
    let amount_2 = 3000;
    let response = contract_instance
        .methods()
        .get(5, 6)
        .add_custom_asset(asset_id_1, amount_1, Some(wallet_2.address().clone()))
        .add_custom_asset(asset_id_2, amount_2, Some(wallet_2.address().clone()))
        .call()
        .await?;

    assert_eq!(response.value, 11);

    let balance_asset_1 = wallet_1.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_1.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount - amount_1);
    assert_eq!(balance_asset_2, initial_amount - amount_2);

    let balance_asset_1 = wallet_2.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_2.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount + amount_1);
    assert_eq!(balance_asset_2, initial_amount + amount_2);

    Ok(())
}

#[tokio::test]
async fn contract_load_error_messages() {
    {
        let binary_path = "sway/contracts/contract_test/out/release/no_file_on_path.bin";
        let expected_error = format!("io: file \"{binary_path}\" does not exist");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
    {
        let binary_path = "sway/contracts/contract_test/out/release/contract_test-abi.json";
        let expected_error = format!("expected \"{binary_path}\" to have '.bin' extension");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
}

#[tokio::test]
async fn test_payable_annotation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/payable_annotation"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .payable()
        .call_params(
            CallParameters::default()
                .with_amount(100)
                .with_gas_forwarded(20_000),
        )?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    // ANCHOR: non_payable_params
    let err = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_amount(100))
        .expect_err("should return error");

    assert!(matches!(err, Error::Other(s) if s.contains("assets forwarded to non-payable method")));
    // ANCHOR_END: non_payable_params

    let response = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_gas_forwarded(20_000))?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    Ok(())
}

#[tokio::test]
async fn multi_call_from_calls_with_different_account_types() -> Result<()> {
    use fuels::prelude::*;

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallet = WalletUnlocked::new_random(None);
    let predicate = Predicate::from_code(vec![]);

    let contract_methods_wallet =
        MyContract::new(Bech32ContractId::default(), wallet.clone()).methods();
    let contract_methods_predicate =
        MyContract::new(Bech32ContractId::default(), predicate).methods();

    let call_handler_1 = contract_methods_wallet.initialize_counter(42);
    let call_handler_2 = contract_methods_predicate.get_array([42; 2]);

    let _multi_call_handler = CallHandler::new_multi_call(wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    Ok(())
}

#[tokio::test]
async fn low_level_call() -> Result<()> {
    use fuels::types::SizedAsciiString;

    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyCallerContract",
                project = "e2e/sway/contracts/low_level_caller"
            ),
            Contract(
                name = "MyTargetContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "caller_contract_instance",
            contract = "MyCallerContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "target_contract_instance",
            contract = "MyTargetContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let function_selector = encode_fn_selector("initialize_counter");
    let call_data = calldata!(42u64)?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let response = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await?;
    assert_eq!(response.value, 42);

    let function_selector = encode_fn_selector("set_value_multiple_complex");
    let call_data = calldata!(
        MyStruct {
            a: true,
            b: [1, 2, 3],
        },
        SizedAsciiString::<4>::try_from("fuel")?
    )?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let result_uint = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await
        .unwrap()
        .value;

    let result_bool = target_contract_instance
        .methods()
        .get_bool_value()
        .call()
        .await
        .unwrap()
        .value;

    let result_str = target_contract_instance
        .methods()
        .get_str_value()
        .call()
        .await
        .unwrap()
        .value;

    assert_eq!(result_uint, 42);
    assert!(result_bool);
    assert_eq!(result_str, "fuel");

    Ok(())
}

#[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
#[test]
fn db_rocksdb() {
    use std::{fs, str::FromStr};

    use fuels::{
        accounts::wallet::WalletUnlocked,
        client::{PageDirection, PaginationRequest},
        crypto::SecretKey,
        prelude::{setup_test_provider, DbType, Error, ViewOnlyAccount, DEFAULT_COIN_AMOUNT},
    };

    let temp_dir = tempfile::tempdir().expect("failed to make tempdir");
    let temp_dir_name = temp_dir
        .path()
        .file_name()
        .expect("failed to get file name")
        .to_string_lossy()
        .to_string();
    let temp_database_path = temp_dir.path().join("db");

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let _ = temp_dir;
            let wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            const NUMBER_OF_ASSETS: u64 = 2;
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let chain_config = ChainConfig {
                chain_name: temp_dir_name.clone(),
                consensus_parameters: Default::default(),
                ..ChainConfig::local_testnet()
            };

            let (coins, _) = setup_multiple_assets_coins(
                wallet.address(),
                NUMBER_OF_ASSETS,
                DEFAULT_NUM_COINS,
                DEFAULT_COIN_AMOUNT,
            );

            let provider =
                setup_test_provider(coins.clone(), vec![], Some(node_config), Some(chain_config))
                    .await?;

            provider.produce_blocks(2, None).await?;

            Ok::<(), Error>(())
        })
        .unwrap();

    // The runtime needs to be terminated because the node can currently only be killed when the runtime itself shuts down.

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let provider = setup_test_provider(vec![], vec![], Some(node_config), None).await?;
            // the same wallet that was used when rocksdb was built. When we connect it to the provider, we expect it to have the same amount of assets
            let mut wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            wallet.set_provider(provider.clone());

            let blocks = provider
                .get_blocks(PaginationRequest {
                    cursor: None,
                    results: 10,
                    direction: PageDirection::Forward,
                })
                .await?
                .results;

            assert_eq!(blocks.len(), 3);
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(wallet.get_balances().await?.len(), 2);

            fs::remove_dir_all(
                temp_database_path
                    .parent()
                    .expect("db parent folder does not exist"),
            )?;

            Ok::<(), Error>(())
        })
        .unwrap();
}

#[tokio::test]
async fn can_configure_decoding_of_contract_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: Will not work if max_tokens not big enough
        methods.i_return_a_1k_el_array().with_decoder_config(DecoderConfig{max_tokens: 100, ..Default::default()}).call().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Single call: Works when limit is bumped
        let result = methods
            .i_return_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(result, [0; 1000]);
    }
    {
        // Multi call: Will not work if max_tokens not big enough
        CallHandler::new_multi_call(wallet.clone())
         .add_call(methods.i_return_a_1k_el_array())
         .with_decoder_config(DecoderConfig { max_tokens: 100, ..Default::default() })
         .call::<([u8; 1000],)>().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Multi call: Works when configured
        CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_return_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<([u8; 1000],)>()
            .await
            .unwrap();
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let submitted_tx = contract_methods.get(1, 2).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;

    assert_eq!(value, 3);

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let handle = multi_call_handler.submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let (val_1, val_2): (u64, u64) = handle.response().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_heap_type_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vector_output"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        let call_handler_1 = contract_instance.methods().u8_in_vec(5);
        let call_handler_2 = contract_instance_2.methods().get_single(7);
        let call_handler_3 = contract_instance.methods().u8_in_vec(3);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2)
            .add_call(call_handler_3);

        let (val_1, val_2, val_3): (Vec<u8>, u64, Vec<u8>) = multi_call_handler.call().await?.value;

        assert_eq!(val_1, vec![0, 1, 2, 3, 4]);
        assert_eq!(val_2, 7);
        assert_eq!(val_3, vec![0, 1, 2]);
    }

    Ok(())
}

#[tokio::test]
async fn heap_types_correctly_offset_in_create_transactions_w_storage_slots() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Predicate(
            name = "MyPredicate",
            project = "e2e/sway/types/predicates/predicate_vector"
        ),),
    );

    let provider = wallet.try_provider()?.clone();
    let data = MyPredicateEncoder::default().encode_data(18, 24, vec![2, 4, 42])?;
    let predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(data)
    .with_provider(provider);

    wallet
        .transfer(
            predicate.address(),
            10_000,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await?;

    // if the contract is successfully deployed then the predicate was unlocked. This further means
    // the offsets were setup correctly since the predicate uses heap types in its arguments.
    // Storage slots were loaded automatically by default
    Contract::load_from(
        "sway/contracts/storage/out/release/storage.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    Ok(())
}

#[tokio::test]
async fn test_arguments_with_gas_forwarded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vectors"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let x = 128;
    let vec_input = vec![0, 1, 2];
    {
        let response = contract_instance
            .methods()
            .get_single(x)
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;

        assert_eq!(response.value, x);
    }
    {
        contract_instance_2
            .methods()
            .u32_vec(vec_input.clone())
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;
    }
    {
        let call_handler_1 = contract_instance.methods().get_single(x);
        let call_handler_2 = contract_instance_2.methods().u32_vec(vec_input);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let (value, _): (u64, ()) = multi_call_handler.call().await?.value;

        assert_eq!(value, x);
    }

    Ok(())
}

#[tokio::test]
async fn contract_custom_call_no_signatures_strategy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let provider = wallet.try_provider()?;

    let counter = 42;
    let call_handler = contract_instance.methods().initialize_counter(counter);

    let mut tb = call_handler.transaction_builder().await?;

    let base_asset_id = *provider.consensus_parameters().await?.base_asset_id();

    let amount = 10;
    let consensus_parameters = provider.consensus_parameters().await?;
    let new_base_inputs = wallet
        .get_asset_inputs_for_amount(base_asset_id, amount, None)
        .await?;
    tb.inputs_mut().extend(new_base_inputs);
    tb.outputs_mut()
        .push(Output::change(wallet.address().into(), 0, base_asset_id));

    // ANCHOR: tb_no_signatures_strategy
    let mut tx = tb
        .with_build_strategy(ScriptBuildStrategy::NoSignatures)
        .build(provider)
        .await?;
    // ANCHOR: tx_sign_with
    tx.sign_with(&wallet, consensus_parameters.chain_id())
        .await?;
    // ANCHOR_END: tx_sign_with
    // ANCHOR_END: tb_no_signatures_strategy

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;

    let tx_status = provider.tx_status(&tx_id).await?;

    let response = call_handler.get_response_from(tx_status)?;

    assert_eq!(counter, response.value);

    Ok(())
}

#[tokio::test]
async fn contract_encoder_config_is_applied() -> Result<()> {
    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Wallets("wallet")
    );
    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let instance = TestContract::new(contract_id.clone(), wallet.clone());

    {
        let _encoding_ok = instance
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let instance_with_encoder_config = instance.with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));

        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));
    }

    Ok(())
}

#[tokio::test]
async fn test_reentrant_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LibContractCaller",
            project = "e2e/sway/contracts/lib_contract_caller"
        ),),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = contract_caller_instance.contract_id();
    let response = contract_caller_instance
        .methods()
        .re_entrant(contract_id, true)
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn msg_sender_gas_estimation_issue() {
    // Gas estimation requires an input of the base asset. If absent, a fake input is
    // added. However, if a non-base coin is present and the fake input introduces a
    // second owner, it causes the `msg_sender` sway fn to fail. This leads
    // to a premature failure in gas estimation, risking transaction failure due to
    // a low gas limit.
    let mut wallet = WalletUnlocked::new_random(None);

    let (coins, ids) =
        setup_multiple_assets_coins(wallet.address(), 2, DEFAULT_NUM_COINS, DEFAULT_COIN_AMOUNT);

    let provider = setup_test_provider(coins, vec![], None, None)
        .await
        .unwrap();
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/msg_methods"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let asset_id = ids[0];

    // The fake coin won't be added if we add a base asset, so let's not do that
    assert!(
        asset_id
            != *provider
                .consensus_parameters()
                .await
                .unwrap()
                .base_asset_id()
    );
    let call_params = CallParameters::default()
        .with_amount(100)
        .with_asset_id(asset_id);

    contract_instance
        .methods()
        .message_sender()
        .call_params(call_params)
        .unwrap()
        .call()
        .await
        .unwrap();
}

#[tokio::test]
async fn variable_output_estimation_is_optimized() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/var_outputs"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_methods = contract_instance.methods();

    let coins = 252;
    let recipient = Identity::Address(wallet.address().into());
    let start = Instant::now();
    let _ = contract_methods
        .mint(coins, recipient)
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    // debug builds are slower (20x for `fuel-core-lib`, 4x for a release-fuel-core-binary)
    // we won't validate in that case so we don't have to maintain two expectations
    if !cfg!(debug_assertions) {
        let elapsed = start.elapsed().as_secs();
        let limit = 2;
        if elapsed > limit {
            panic!("Estimation took too long ({elapsed}). Limit is {limit}");
        }
    }

    Ok(())
}

async fn setup_node_with_high_price() -> Result<Vec<WalletUnlocked>> {
    let wallet_config = WalletsConfig::new(None, None, None);
    let fee_parameters = FeeParameters::V1(FeeParametersV1 {
        gas_price_factor: 92000,
        gas_per_byte: 63,
    });
    let consensus_parameters = ConsensusParameters::V1(ConsensusParametersV1 {
        fee_params: fee_parameters,
        ..Default::default()
    });
    let node_config = Some(NodeConfig {
        starting_gas_price: 1100,
        ..NodeConfig::default()
    });
    let chain_config = ChainConfig {
        consensus_parameters,
        ..ChainConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(wallet_config, node_config, Some(chain_config))
            .await?;

    Ok(wallets)
}

#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);

    let response = MyContract::new(contract_id, no_funds_wallet.clone())
        .methods()
        .get(5, 6)
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn simulations_can_be_made_without_coins_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let contract_instance = MyContract::new(contract_id, no_funds_wallet.clone());

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.get(1, 2);
    let call_handler_2 = contract_methods.get(3, 4);

    let mut multi_call_handler = CallHandler::new_multi_call(no_funds_wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let value: (u64, u64) = multi_call_handler
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value, (3, 7));

    Ok(())
}

#[tokio::test]
async fn contract_call_with_non_zero_base_asset_id_and_tip() -> Result<()> {
    use fuels::{prelude::*, tx::ConsensusParameters};

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let asset_id = AssetId::new([1; 32]);

    let mut consensus_parameters = ConsensusParameters::default();
    consensus_parameters.set_base_asset_id(asset_id);

    let config = ChainConfig {
        consensus_parameters,
        ..Default::default()
    };

    let asset_base = AssetConfig {
        id: asset_id,
        num_coins: 1,
        coin_amount: 10_000,
    };

    let wallet_config = WalletsConfig::new_multiple_assets(1, vec![asset_base]);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, Some(config)).await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_tip(10))
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn max_fee_estimation_respects_tolerance() -> Result<()> {
    use fuels::prelude::*;

    let mut call_wallet = WalletUnlocked::new_random(None);

    let call_coins = setup_single_asset_coins(call_wallet.address(), AssetId::BASE, 1000, 1);

    let mut deploy_wallet = WalletUnlocked::new_random(None);
    let deploy_coins =
        setup_single_asset_coins(deploy_wallet.address(), AssetId::BASE, 1, 1_000_000);

    let provider =
        setup_test_provider([call_coins, deploy_coins].concat(), vec![], None, None).await?;

    call_wallet.set_provider(provider.clone());
    deploy_wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            wallet = "deploy_wallet",
            contract = "MyContract",
            random_salt = false,
        )
    );
    let contract_instance = contract_instance.with_account(call_wallet.clone());

    let max_fee_from_tx = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            let builder = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap();

            assert_eq!(
                builder.max_fee_estimation_tolerance, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
                "Expected pre-set tolerance"
            );

            builder
                .with_max_fee_estimation_tolerance(tolerance)
                .build(&provider)
                .await
                .unwrap()
                .max_fee()
                .unwrap()
        }
    };

    let max_fee_from_builder = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance)
                .estimate_max_fee(&provider)
                .await
                .unwrap()
        }
    };

    let base_amount_in_inputs = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let call_wallet = &call_wallet;
        async move {
            let mut tb = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance);

            call_wallet.adjust_for_fee(&mut tb, 0).await.unwrap();
            tb.inputs
                .iter()
                .filter_map(|input: &Input| match input {
                    Input::ResourceSigned { resource }
                        if resource.coin_asset_id().unwrap() == AssetId::BASE =>
                    {
                        Some(resource.amount())
                    }
                    _ => None,
                })
                .sum::<u64>()
        }
    };

    let no_increase_max_fee = max_fee_from_tx(0.0).await;
    let increased_max_fee = max_fee_from_tx(2.00).await;

    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let no_increase_max_fee = max_fee_from_builder(0.0).await;
    let increased_max_fee = max_fee_from_builder(2.00).await;
    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let normal_base_asset = base_amount_in_inputs(0.0).await;
    let more_base_asset_due_to_bigger_tolerance = base_amount_in_inputs(5.00).await;
    assert!(more_base_asset_due_to_bigger_tolerance > normal_base_asset);

    Ok(())
}

#[tokio::test]
async fn blob_contract_deployment() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
    ));

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract_size = std::fs::metadata(contract_binary)
        .expect("contract file not found")
        .len();

    assert!(
         contract_size > 150_000,
         "the testnet size limit was around 100kB, we want a contract bigger than that to reflect prod (current: {contract_size}B)"
     );

    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::new(Some(2), None, None), None, None)
            .await?;

    let provider = wallets[0].provider().unwrap().clone();

    let consensus_parameters = provider.consensus_parameters().await?;

    let contract_max_size = consensus_parameters.contract_params().contract_max_size();
    assert!(
         contract_size > contract_max_size,
         "this test should ideally be run with a contract bigger than the max contract size ({contract_max_size}B) so that we know deployment couldn't have happened without blobs"
     );

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallets[0], TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn regular_contract_can_be_deployed() -> Result<()> {
    // given
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
    );

    let contract_binary = "sway/contracts/contract_test/out/release/contract_test.bin";

    // when
    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    // then
    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance
        .methods()
        .get_counter()
        .call()
        .await?
        .value;

    assert_eq!(response, 0);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_be_deployed_directly() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_upload_blobs_separately_then_deploy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    let blob_ids = contract.blob_ids();

    // if this were an example for the user we'd just call `deploy` on the contract above
    // this way we are testing that the blobs were really deployed above, otherwise the following
    // would fail
    let contract_id = Contract::loader_from_blob_ids(
        blob_ids.to_vec(),
        contract.salt(),
        contract.storage_slots().to_vec(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_blob_already_uploaded_not_an_issue() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?;

    // this will upload blobs
    contract
        .clone()
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    // this will try to upload the blobs but skip upon encountering an error
    let contract_id = contract
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .something()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_storage_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_storage_slots = contract.storage_slots().to_vec();

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";
    let proxy_contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let combined_storage_slots = [&contract_storage_slots, proxy_contract.storage_slots()].concat();

    let proxy_id = proxy_contract
        .with_storage_slots(combined_storage_slots)
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?
        .value;

    assert_eq!(response, 42);

    let _res = proxy
        .methods()
        .write_some_u64(36)
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 36);

    Ok(())
}\n```

Note In contrast to adding signers to a transaction builder, when signing a built transaction, you must ensure that the order of signatures matches the order of signed inputs. Multiple signed inputs with the same owner will have the same witness index.

Custom contract and script calls

When preparing a contract call via CallHandler, the Rust SDK uses a transaction builder in the background. You can fetch this builder and customize it before submitting it to the network. After the transaction is executed successfully, you can use the corresponding CallHandler to generate a call response. The call response can be used to decode return values and logs. Below are examples for both contract and script calls.

Custom contract call

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Custom script call

```rust\nuse std::time::Duration;

use fuel_tx::Output;
use fuels::{
    client::{PageDirection, PaginationRequest},
    core::{
        codec::{DecoderConfig, EncoderConfig},
        traits::Tokenizable,
        Configurables,
    },
    prelude::*,
    programs::{executable::Executable, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE},
    types::{Bits256, Identity},
};

#[tokio::test]
async fn main_function_arguments() -> Result<()> {
    // ANCHOR: script_with_arguments
    // The abigen is used for the same purpose as with contracts (Rust bindings)
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/scripts/arguments/out/release/arguments.bin";
    let script_instance = MyScript::new(wallet, bin_path);

    let bim = Bimbam { val: 90 };
    let bam = SugarySnack {
        twix: 100,
        mars: 1000,
    };

    let result = script_instance.main(bim, bam).call().await?;

    let expected = Bimbam { val: 2190 };
    assert_eq!(result.value, expected);
    // ANCHOR_END: script_with_arguments
    Ok(())
}

#[tokio::test]
async fn script_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let a = 4u64;
    let b = 2u32;
    let estimated_gas_used = script_instance
        .main(a, b)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = script_instance.main(a, b).call().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);

    Ok(())
}

#[tokio::test]
async fn test_basic_script_with_tx_policies() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "bimbam_script",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "bimbam_script",
            wallet = "wallet"
        )
    );

    let a = 1000u64;
    let b = 2000u32;
    let result = script_instance.main(a, b).call().await?;
    assert_eq!(result.value, "hello");

    // ANCHOR: script_with_tx_policies
    let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
    let result = script_instance
        .main(a, b)
        .with_tx_policies(tx_policies)
        .call()
        .await?;
    // ANCHOR_END: script_with_tx_policies
    assert_eq!(result.value, "hello");

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "transfer_script",
            project = "e2e/sway/scripts/transfer_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "transfer_script",
            wallet = "wallet"
        )
    );

    let provider = wallet.try_provider()?.clone();
    let mut receiver = WalletUnlocked::new_random(None);
    receiver.set_provider(provider);

    let amount = 1000;
    let asset_id = AssetId::zeroed();
    let script_call = script_instance.main(
        amount,
        asset_id,
        Identity::Address(receiver.address().into()),
    );
    let inputs = wallet
        .get_asset_inputs_for_amount(asset_id, amount, None)
        .await?;
    let output = Output::change(wallet.address().into(), 0, asset_id);
    let _ = script_call
        .with_inputs(inputs)
        .with_outputs(vec![output])
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    let receiver_balance = receiver.get_asset_balance(&asset_id).await?;
    assert_eq!(receiver_balance, amount);

    Ok(())
}

#[tokio::test]
async fn test_script_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };
    let response = script_instance.main(my_struct).call().await?;

    assert_eq!(response.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_enum"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_enum = MyEnum::Two;
    let response = script_instance.main(my_enum).call().await?;

    assert_eq!(response.value, 2);
    Ok(())
}

#[tokio::test]
async fn test_script_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_array"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_array: [u64; 4] = [1, 2, 3, 4];
    let response = script_instance.main(my_array).call().await?;

    assert_eq!(response.value, 10);
    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_on_script_call() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_needs_custom_decoder"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    {
        // Will fail if max_tokens too low
        script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 101,
                ..Default::default()
            })
            .call()
            .await
            .expect_err(
                "Should fail because return type has more tokens than what is allowed by default",
            );
    }
    {
        // When the token limit is bumped should pass
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(response, [0u8; 1000]);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/script_struct"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );

    let my_struct = MyStruct {
        number: 42,
        boolean: true,
    };

    // ANCHOR: submit_response_script
    let submitted_tx = script_instance.main(my_struct).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;
    // ANCHOR_END: submit_response_script

    assert_eq!(value, 42);
    Ok(())
}

#[tokio::test]
async fn test_script_transaction_builder() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.try_provider()?;

    // ANCHOR: script_call_tb
    let script_call_handler = script_instance.main(1, 2);

    let mut tb = script_call_handler.transaction_builder().await?;

    // customize the builder...

    wallet.adjust_for_fee(&mut tb, 0).await?;
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(provider).await?;

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let tx_status = provider.tx_status(&tx_id).await?;

    let response = script_call_handler.get_response_from(tx_status)?;

    assert_eq!(response.value, "hello");
    // ANCHOR_END: script_call_tb

    Ok(())
}

#[tokio::test]
async fn script_encoder_config_is_applied() {
    abigen!(Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/basic_script/out/release/basic_script-abi.json"
    ));
    let wallet = launch_provider_and_get_wallet().await.expect("");
    let bin_path = "sway/scripts/basic_script/out/release/basic_script.bin";

    let script_instance_without_encoder_config = MyScript::new(wallet.clone(), bin_path);
    {
        let _encoding_ok = script_instance_without_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let script_instance_with_encoder_config =
            MyScript::new(wallet.clone(), bin_path).with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));

        let encoding_error = script_instance_with_encoder_config
            .main(1, 2)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode script call arguments: codec: token limit `1` reached while encoding"
        ));
    }
}
#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "MyScript",
            project = "e2e/sway/scripts/basic_script"
        )),
        LoadScript(
            name = "script_instance",
            script = "MyScript",
            wallet = "wallet"
        )
    );
    let provider = wallet.provider().cloned();

    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let script_instance = script_instance.with_account(no_funds_wallet);

    let value = script_instance
        .main(1000, 2000)
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value.as_ref(), "hello");

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_builder() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    // ANCHOR: preload_low_level
    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular
        .convert_to_loader()?
        .with_configurables(configurables);

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;
    // ANCHOR_END: preload_low_level

    Ok(())
}

#[tokio::test]
async fn can_be_run_in_blobs_high_level() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let mut my_script = my_script.with_configurables(configurables);

    let arg = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    };
    let secret = my_script
        .convert_into_loader()
        .await?
        .main(arg)
        .call()
        .await?
        .value;

    assert_eq!(secret, 10001);

    Ok(())
}

#[tokio::test]
async fn high_level_blob_upload_sets_max_fee_tolerance() -> Result<()> {
    let node_config = NodeConfig {
        starting_gas_price: 1000000000,
        ..Default::default()
    };
    let mut wallet = WalletUnlocked::new_random(None);
    let coins = setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, u64::MAX);
    let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/script_blobs",
            name = "MyScript"
        )),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let loader = Executable::from_bytes(std::fs::read(
        "sway/scripts/script_blobs/out/release/script_blobs.bin",
    )?)
    .convert_to_loader()?;

    let zero_tolerance_fee = {
        let mut tb = BlobTransactionBuilder::default()
            .with_blob(loader.blob())
            .with_max_fee_estimation_tolerance(0.);

        wallet.adjust_for_fee(&mut tb, 0).await?;

        wallet.add_witnesses(&mut tb)?;
        let tx = tb.build(&provider).await?;
        tx.max_fee().unwrap()
    };

    let mut my_script = my_script;
    my_script.convert_into_loader().await?;

    let max_fee_of_sent_blob_tx = provider
        .get_transactions(PaginationRequest {
            cursor: None,
            results: 100,
            direction: PageDirection::Forward,
        })
        .await?
        .results
        .into_iter()
        .find_map(|tx| {
            if let TransactionType::Blob(blob_transaction) = tx.transaction {
                blob_transaction.max_fee()
            } else {
                None
            }
        })
        .unwrap();

    assert_eq!(
        max_fee_of_sent_blob_tx,
        (zero_tolerance_fee as f32 * (1.0 + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)).ceil() as u64,
        "the blob upload tx should have had the max fee increased by the default estimation tolerance"
    );

    Ok(())
}

#[tokio::test]
async fn no_data_section_blob_run() -> Result<()> {
    setup_program_test!(
        Abigen(Script(
            project = "e2e/sway/scripts/empty",
            name = "MyScript"
        )),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let mut my_script = my_script;

    // ANCHOR: preload_high_level
    my_script.convert_into_loader().await?.main().call().await?;
    // ANCHOR_END: preload_high_level

    Ok(())
}

#[tokio::test]
async fn loader_script_calling_loader_proxy() -> Result<()> {
    setup_program_test!(
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            ),
            Contract(name = "MyProxy", project = "e2e/sway/contracts/proxy"),
            Script(name = "MyScript", project = "e2e/sway/scripts/script_proxy"),
        ),
        Wallets("wallet"),
        LoadScript(name = "my_script", script = "MyScript", wallet = "wallet")
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id.clone(), wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let mut my_script = my_script;
    let result = my_script
        .convert_into_loader()
        .await?
        .main(proxy_id.clone())
        .with_contract_ids(&[contract_id, proxy_id])
        .call()
        .await?;

    assert!(result.value);

    Ok(())
}

#[tokio::test]
async fn loader_can_be_presented_as_a_normal_script_with_shifted_configurables() -> Result<()> {
    abigen!(Script(
        abi = "e2e/sway/scripts/script_blobs/out/release/script_blobs-abi.json",
        name = "MyScript"
    ));

    let binary_path = "./sway/scripts/script_blobs/out/release/script_blobs.bin";
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?.clone();

    let regular = Executable::load_from(binary_path)?;

    let configurables = MyScriptConfigurables::default().with_SECRET_NUMBER(10001)?;
    let loader = regular.clone().convert_to_loader()?;

    // The Blob must be uploaded manually, otherwise the script code will revert.
    loader.upload_blob(wallet.clone()).await?;

    let encoder = fuels::core::codec::ABIEncoder::default();
    let token = MyStruct {
        field_a: MyEnum::B(99),
        field_b: Bits256([17; 32]),
    }
    .into_token();
    let data = encoder.encode(&[token])?;

    let configurables: Configurables = configurables.into();

    let shifted_configurables = configurables
        .with_shifted_offsets(-(regular.data_offset_in_code().unwrap() as i64))
        .unwrap()
        .with_shifted_offsets(loader.data_offset_in_code() as i64)
        .unwrap();

    let loader_posing_as_normal_script =
        Executable::from_bytes(loader.code()).with_configurables(shifted_configurables);

    let mut tb = ScriptTransactionBuilder::default()
        .with_script(loader_posing_as_normal_script.code())
        .with_script_data(data);

    wallet.adjust_for_fee(&mut tb, 0).await?;

    wallet.add_witnesses(&mut tb)?;

    let tx = tb.build(&provider).await?;

    let response = provider.send_transaction_and_await_commit(tx).await?;

    response.check(None)?;

    Ok(())
}\n```

Types

The FuelVM and Sway have many internal types. These types have equivalents in the SDK. This section discusses these types, how to use them, and how to convert them.

Bytes32

In Sway and the FuelVM, Bytes32 represents hashes. They hold a 256-bit (32-byte) value. Bytes32 is a wrapper on a 32-sized slice of u8: pub struct Bytes32([u8; 32]);.

These are the main ways of creating a Bytes32:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Bytes32 also implements the fmt module's Debug, Display, LowerHex and UpperHex traits. For example, you can get the display and hex representations with:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

For a full list of implemented methods and traits, see the fuel-types documentation.

Note: In Fuel, there's a special type called b256, which is similar to Bytes32; also used to represent hashes, and it holds a 256-bit value. In Rust, through the SDK, this is represented as Bits256(value) where value is a [u8; 32]. If your contract method takes a b256 as input, all you need to do is pass a Bits256([u8; 32]) when calling it from the SDK.

Address

Like Bytes32, Address is a wrapper on [u8; 32] with similar methods and implements the same traits (see fuel-types documentation).

These are the main ways of creating an Address:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

ContractId

Like Bytes32, ContractId is a wrapper on [u8; 32] with similar methods and implements the same traits (see fuel-types documentation).

These are the main ways of creating a ContractId:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

AssetId

Like Bytes32, AssetId is a wrapper on [u8; 32] with similar methods and implements the same traits (see fuel-types documentation).

These are the main ways of creating an AssetId:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Bech32

Bech32Address and Bech32ContractId enable the use of addresses and contract IDs in the bech32 format. They can easily be converted to their counterparts Address and ContractId.

Here are the main ways of creating a Bech32Address, but note that the same applies to Bech32ContractId:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Note: when creating a Bech32Address from Address or Bech32ContractId from ContractId the HRP (Human-Readable Part) is set to "fuel" per default.

Structs and enums

The structs and enums you define in your Sway code have equivalents automatically generated by the SDK's abigen! macro.

For instance, if in your Sway code you have a struct called CounterConfig that looks like this:

struct CounterConfig {
  dummy: bool,
  initial_value: u64,
}

After using the abigen! macro, CounterConfig will be accessible in your Rust file! Here's an example:

```rust\nuse std::str::FromStr;

use fuels::{
    prelude::*,
    types::{Bits256, EvmAddress, Identity, SizedAsciiString, B512, U256},
};

pub fn null_contract_id() -> Bech32ContractId {
    // a bech32 contract address that decodes to [0u8;32]
    Bech32ContractId::from_str("fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2")
        .expect("is valid")
}

#[tokio::test]
async fn test_methods_typeless_argument() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/empty_arguments"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .method_with_empty_argument()
        .call()
        .await?;

    assert_eq!(response.value, 63);

    Ok(())
}

#[tokio::test]
async fn call_with_empty_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/types/contracts/call_empty_return"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let _response = contract_instance.methods().store_value(42).call().await?;
    Ok(())
}

#[tokio::test]
async fn call_with_structs() -> Result<()> {
    // Generates the bindings from the an ABI definition inline.
    // The generated bindings can be accessed through `MyContract`.
    // ANCHOR: struct_generation
    abigen!(Contract(name="MyContract",
                     abi="e2e/sway/types/contracts/complex_types_contract/out/release/complex_types_contract-abi.json"));

    // Here we can use `CounterConfig`, a struct originally
    // defined in the contract.
    let counter_config = CounterConfig {
        dummy: true,
        initial_value: 42,
    };
    // ANCHOR_END: struct_generation

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/types/contracts/complex_types_contract/out/release/complex_types_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id, wallet).methods();

    let response = contract_methods
        .initialize_counter(counter_config)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let response = contract_methods.increment_counter(10).call().await?;

    assert_eq!(52, response.value);

    Ok(())
}

#[tokio::test]
async fn abigen_different_structs_same_arg_name() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/two_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let param_one = StructOne { foo: 42 };
    let param_two = StructTwo { bar: 42 };

    let contract_methods = contract_instance.methods();
    let res_one = contract_methods.something(param_one).call().await?;

    assert_eq!(res_one.value, 43);

    let res_two = contract_methods.something_else(param_two).call().await?;

    assert_eq!(res_two.value, 41);
    Ok(())
}

#[tokio::test]
async fn nested_structs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/nested_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = AllStruct {
        some_struct: SomeStruct {
            field: 12345,
            field_2: true,
        },
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_struct().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_struct_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the argument correctly. Investigate!"
    );

    let memory_address = MemoryAddress {
        contract_id: ContractId::zeroed(),
        function_selector: 10,
        function_data: 0,
    };

    let call_data = CallData {
        memory_address,
        num_coins_to_forward: 10,
        asset_id_of_coins_to_forward: ContractId::zeroed(),
        amount_of_gas_to_forward: 5,
    };

    let actual = contract_methods
        .nested_struct_with_reserved_keyword_substring(call_data.clone())
        .call()
        .await?
        .value;

    assert_eq!(actual, call_data);
    Ok(())
}

#[tokio::test]
async fn calls_with_empty_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/complex_types_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.get_empty_struct().call().await?;

        assert_eq!(response.value, EmptyStruct {});
    }
    {
        let response = contract_methods
            .input_empty_struct(EmptyStruct {})
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_struct_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let cocktail_in_bytes: Vec<u8> = vec![
        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3,
    ];

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(2),
        glass: 3,
    };

    // as slice
    let actual: Cocktail = cocktail_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Cocktail = (&cocktail_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Cocktail = cocktail_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn test_tuples() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/tuples"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.returns_tuple((1, 2)).call().await?;

        assert_eq!(response.value, (1, 2));
    }
    {
        // Tuple with struct.
        let my_struct_tuple = (
            42,
            Person {
                name: "Jane".try_into()?,
            },
        );
        let response = contract_methods
            .returns_struct_in_tuple(my_struct_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_struct_tuple);
    }
    {
        // Tuple with enum.
        let my_enum_tuple: (u64, State) = (42, State::A);

        let response = contract_methods
            .returns_enum_in_tuple(my_enum_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // Tuple with single element
        let my_enum_tuple = (123u64,);

        let response = contract_methods
            .single_element_tuple(my_enum_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // tuple with b256
        let id = *ContractId::zeroed();
        let my_b256_u8_tuple = (Bits256(id), 10);

        let response = contract_methods
            .tuple_with_b256(my_b256_u8_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_b256_u8_tuple);
    }

    Ok(())
}

#[tokio::test]
async fn test_evm_address() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/evm_address"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        // ANCHOR: evm_address_arg
        let b256 = Bits256::from_hex_str(
            "0x1616060606060606060606060606060606060606060606060606060606060606",
        )?;
        let evm_address = EvmAddress::from(b256);

        let call_handler = contract_instance
            .methods()
            .evm_address_as_input(evm_address);
        // ANCHOR_END: evm_address_arg

        assert!(call_handler.call().await?.value);
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_literal()
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_argument(b256)
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        contract_instance
            .methods()
            .get_array([42; 2])
            .call()
            .await?
            .value,
        [42; 2]
    );
    Ok(())
}

#[tokio::test]
async fn test_arrays_with_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let persons = [
        Person {
            name: "John".try_into()?,
        },
        Person {
            name: "Jane".try_into()?,
        },
    ];

    let contract_methods = contract_instance.methods();
    let response = contract_methods.array_of_structs(persons).call().await?;

    assert_eq!("John", response.value[0].name);
    assert_eq!("Jane", response.value[1].name);

    let states = [State::A, State::B];

    let response = contract_methods
        .array_of_enums(states.clone())
        .call()
        .await?;

    assert_eq!(states[0], response.value[0]);
    assert_eq!(states[1], response.value[1]);
    Ok(())
}

#[tokio::test]
async fn str_in_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/str_in_array"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let input = ["foo", "bar", "baz"].map(|str| str.try_into().unwrap());
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .take_array_string_shuffle(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["baz", "foo", "bar"]);

    let response = contract_methods
        .take_array_string_return_single(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["foo"]);

    let response = contract_methods
        .take_array_string_return_single_element(input)
        .call()
        .await?;

    assert_eq!(response.value, "bar");
    Ok(())
}

#[tokio::test]
async fn test_enum_inside_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_inside_struct"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(11),
        glass: 333,
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .return_enum_inside_struct(11)
        .call()
        .await?;

    assert_eq!(response.value, expected);

    let enum_inside_struct = Cocktail {
        the_thing_you_mix_in: Shaker::Cosmopolitan(444),
        glass: 555,
    };

    let response = contract_methods
        .take_enum_inside_struct(enum_inside_struct)
        .call()
        .await?;

    assert_eq!(response.value, 555);
    Ok(())
}

#[tokio::test]
async fn native_types_support() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/native_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let user = User {
        weight: 10,
        address: Address::zeroed(),
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods.wrapped_address(user).call().await?;

    assert_eq!(response.value.address, Address::zeroed());

    let response = contract_methods
        .unwrapped_address(Address::zeroed())
        .call()
        .await?;

    assert_eq!(
        response.value,
        Address::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")?
    );

    Ok(())
}

#[tokio::test]
async fn enum_coding_w_variable_width_variants() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of enum encoding width, then we'll
    // probably end up mangling arg_2 and onward which will fail this test.
    let expected = BigBundle {
        arg_1: EnumThatHasABigAndSmallVariant::Small(12345),
        arg_2: 6666,
        arg_3: 7777,
        arg_4: 8888,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_big_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_big_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_coding_w_unit_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of unit enum encoding width, then
    // we'll end up mangling arg_2
    let expected = UnitBundle {
        arg_1: UnitEnum::var2,
        arg_2: u64::MAX,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_unit_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_as_input"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = MaxedOutVariantsEnum::Variant255(11);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_max_variant().call().await?.value;
    assert_eq!(expected, actual);

    let expected = StandardEnum::Two(12345);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_standard_enum().call().await?.value;
    assert_eq!(expected, actual);

    let fuelvm_judgement = contract_methods
        .check_standard_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the standard enum correctly. Investigate!"
    );

    let expected = UnitEnum::Two;
    let actual = contract_methods.get_unit_enum().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the unit enum correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_enum_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let shaker_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];

    let expected = Shaker::Mojito(2);

    // as slice
    let actual: Shaker = shaker_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Shaker = (&shaker_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Shaker = shaker_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn type_inside_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/type_inside_enum"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // String inside enum
    let enum_string = SomeEnum::SomeStr("asdf".try_into()?);
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .str_inside_enum(enum_string.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_string);

    // Array inside enum
    let enum_array = SomeEnum::SomeArr([1, 2, 3, 4]);
    let response = contract_methods
        .arr_inside_enum(enum_array.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_array);

    // Struct inside enum
    let response = contract_methods
        .return_struct_inside_enum(11)
        .call()
        .await?;
    let expected = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 11 });
    assert_eq!(response.value, expected);

    let struct_inside_enum = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 66 });
    let response = contract_methods
        .take_struct_inside_enum(struct_inside_enum)
        .call()
        .await?;
    assert_eq!(response.value, 8888);

    // Enum inside enum
    let expected_enum = EnumLevel3::El2(EnumLevel2::El1(EnumLevel1::Num(42)));
    let response = contract_methods.get_nested_enum().call().await?;
    assert_eq!(response.value, expected_enum);

    let response = contract_methods
        .check_nested_enum_integrity(expected_enum)
        .call()
        .await?;
    assert!(
        response.value,
        "The FuelVM deems that we've not encoded the nested enum correctly. Investigate!"
    );

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_some_address = Some(expected_address);
    let response = contract_methods.get_some_address().call().await?;

    assert_eq!(response.value, expected_some_address);

    let expected_some_u64 = Some(10);
    let response = contract_methods.get_some_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_some_struct().call().await?;
    assert_eq!(response.value, Some(s.clone()));

    let response = contract_methods.get_some_enum().call().await?;
    assert_eq!(response.value, Some(e.clone()));

    let response = contract_methods.get_some_tuple().call().await?;
    assert_eq!(response.value, Some((s.clone(), e.clone())));

    let expected_none = None;
    let response = contract_methods.get_none().call().await?;

    assert_eq!(response.value, expected_none);

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_u64 = Some(36);
    let response = contract_methods
        .input_primitive(expected_u64)
        .call()
        .await?;

    assert!(response.value);

    let expected_struct = Some(s);
    let response = contract_methods
        .input_struct(expected_struct)
        .call()
        .await?;

    assert!(response.value);

    let expected_enum = Some(e);
    let response = contract_methods.input_enum(expected_enum).call().await?;

    assert!(response.value);

    let expected_none = None;
    let response = contract_methods.input_none(expected_none).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods.get_ok_address().call().await?;

    assert_eq!(response.value, expected_ok_address);

    let expected_some_u64 = Ok(10);
    let response = contract_methods.get_ok_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_ok_struct().call().await?;
    assert_eq!(response.value, Ok(s.clone()));

    let response = contract_methods.get_ok_enum().call().await?;
    assert_eq!(response.value, Ok(e.clone()));

    let response = contract_methods.get_ok_tuple().call().await?;
    assert_eq!(response.value, Ok((s, e)));

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.get_error().call().await?;

    assert_eq!(response.value, expected_error);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods
        .input_ok(expected_ok_address)
        .call()
        .await?;

    assert!(response.value);

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.input_error(expected_error).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_identity_address().call().await?;
    assert_eq!(response.value, Identity::Address(expected_address));

    let response = contract_methods.get_identity_contract_id().call().await?;
    assert_eq!(response.value, Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_struct_with_identity().call().await?;
    assert_eq!(response.value, s.clone());

    let response = contract_methods.get_enum_with_identity().call().await?;
    assert_eq!(response.value, e.clone());

    let response = contract_methods.get_identity_tuple().call().await?;
    assert_eq!(response.value, (s, e));

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods
        .input_identity(Identity::Address(expected_address))
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods
        .input_struct_with_identity(s)
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods.input_enum_with_identity(e).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_with_two_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    {
        let response = contract_instance
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }
    {
        let response = contract_instance2
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn generics_test() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/generics"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: generic
        // simple struct with a single generic param
        let arg1 = SimpleGeneric {
            single_generic_param: 123u64,
        };

        let result = contract_methods
            .struct_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
        // ANCHOR_END: generic
    }
    {
        // struct that delegates the generic param internally
        let arg1 = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "abc".try_into()?,
            },
        };

        let result = contract_methods
            .struct_delegating_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in an array
        let arg1 = StructWArrayGeneric { a: [1u32, 2u32] };

        let result = contract_methods
            .struct_w_generic_in_array(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has a generic struct in an array
        let inner = [
            StructWTwoGenerics {
                a: Bits256([1u8; 32]),
                b: 1,
            },
            StructWTwoGenerics {
                a: Bits256([2u8; 32]),
                b: 2,
            },
            StructWTwoGenerics {
                a: Bits256([3u8; 32]),
                b: 3,
            },
        ];
        let arg1 = StructWArrWGenericStruct { a: inner };

        let result = contract_methods
            .array_with_generic_struct(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in a tuple
        let arg1 = StructWTupleGeneric { a: (1, 2) };

        let result = contract_methods
            .struct_w_generic_in_tuple(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // enum with generic in variant
        let arg1 = EnumWGeneric::B(10);
        let result = contract_methods
            .enum_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        contract_methods
            .unused_generic_args(StructUnusedGeneric::new(15), EnumUnusedGeneric::One(15))
            .call()
            .await?;

        let (the_struct, the_enum) = contract_methods
            .used_and_unused_generic_args(
                StructUsedAndUnusedGenericParams::new(10u8),
                EnumUsedAndUnusedGenericParams::Two(11u8),
            )
            .call()
            .await?
            .value;

        assert_eq!(the_struct.field, 12u8);
        if let EnumUsedAndUnusedGenericParams::Two(val) = the_enum {
            assert_eq!(val, 13)
        } else {
            panic!("Expected the variant EnumUsedAndUnusedGenericParams::Two");
        }
    }
    {
        // complex case
        let pass_through = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "ab".try_into()?,
            },
        };
        let w_arr_generic = StructWArrayGeneric {
            a: [pass_through.clone(), pass_through],
        };

        let arg1 = MegaExample {
            a: ([Bits256([0; 32]), Bits256([0; 32])], "ab".try_into()?),
            b: vec![(
                [EnumWGeneric::B(StructWTupleGeneric {
                    a: (w_arr_generic.clone(), w_arr_generic),
                })],
                10u32,
            )],
        };
        contract_methods.complex_test(arg1.clone()).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_vectors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/vectors"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let methods = contract_instance.methods();

    {
        // vec of u32s
        let arg = vec![0, 1, 2];
        methods.u32_vec(arg).call().await?;
    }
    {
        // vec of vecs of u32s
        let arg = vec![vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_vec(arg.clone()).call().await?;
    }
    {
        // vec of structs
        // ANCHOR: passing_in_vec
        let arg = vec![SomeStruct { a: 0 }, SomeStruct { a: 1 }];
        methods.struct_in_vec(arg.clone()).call().await?;
        // ANCHOR_END: passing_in_vec
    }
    {
        // vec in struct
        let arg = SomeStruct { a: vec![0, 1, 2] };
        methods.vec_in_struct(arg.clone()).call().await?;
    }
    {
        // array in vec
        let arg = vec![[0u64, 1u64], [0u64, 1u64]];
        methods.array_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in array
        let arg = [vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_array(arg.clone()).call().await?;
    }
    {
        // vec in enum
        let arg = SomeEnum::a(vec![0, 1, 2]);
        methods.vec_in_enum(arg.clone()).call().await?;
    }
    {
        // enum in vec
        let arg = vec![SomeEnum::a(0), SomeEnum::a(1)];
        methods.enum_in_vec(arg.clone()).call().await?;
    }
    {
        // tuple in vec
        let arg = vec![(0, 0), (1, 1)];
        methods.tuple_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in tuple
        let arg = (vec![0, 1, 2], vec![0, 1, 2]);
        methods.vec_in_tuple(arg.clone()).call().await?;
    }
    {
        // vec in a vec in a struct in a vec
        let arg = vec![
            SomeStruct {
                a: vec![vec![0, 1, 2], vec![3, 4, 5]],
            },
            SomeStruct {
                a: vec![vec![6, 7, 8], vec![9, 10, 11]],
            },
        ];
        methods
            .vec_in_a_vec_in_a_struct_in_a_vec(arg.clone())
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_b256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        Bits256([2; 32]),
        contract_instance
            .methods()
            .b256_as_output()
            .call()
            .await?
            .value
    );

    {
        // ANCHOR: 256_arg
        let b256 = Bits256([1; 32]);

        let call_handler = contract_instance.methods().b256_as_input(b256);
        // ANCHOR_END: 256_arg

        assert!(call_handler.call().await?.value);
    }

    Ok(())
}

#[tokio::test]
async fn test_b512() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b512"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: b512_example
    let hi_bits = Bits256::from_hex_str(
        "0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c",
    )?;
    let lo_bits = Bits256::from_hex_str(
        "0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
    )?;
    let b512 = B512::from((hi_bits, lo_bits));
    // ANCHOR_END: b512_example

    assert_eq!(b512, contract_methods.b512_as_output().call().await?.value);

    {
        let lo_bits2 = Bits256::from_hex_str(
            "0x54ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
        )?;
        let b512 = B512::from((hi_bits, lo_bits2));

        assert!(contract_methods.b512_as_input(b512).call().await?.value);
    }

    Ok(())
}

fn u128_from(parts: (u64, u64)) -> u128 {
    let bytes: [u8; 16] = [parts.0.to_be_bytes(), parts.1.to_be_bytes()]
        .concat()
        .try_into()
        .unwrap();
    u128::from_be_bytes(bytes)
}

#[tokio::test]
async fn test_u128() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u128"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u128_from((1, 2));

        let actual = contract_methods.u128_sum_and_ret(arg).call().await?.value;

        let expected = arg + u128_from((3, 4));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u128_in_enum_output().call().await?.value;

        let expected = SomeEnum::B(u128_from((4, 4)));
        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u128_from((3, 3)));

        contract_methods.u128_in_enum_input(input).call().await?;
    }

    Ok(())
}

fn u256_from(parts: (u64, u64, u64, u64)) -> U256 {
    let bytes: [u8; 32] = [
        parts.0.to_be_bytes(),
        parts.1.to_be_bytes(),
        parts.2.to_be_bytes(),
        parts.3.to_be_bytes(),
    ]
    .concat()
    .try_into()
    .unwrap();
    U256::from(bytes)
}

#[tokio::test]
async fn test_u256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u256_from((1, 2, 3, 4));
        let actual = contract_methods.u256_sum_and_ret(arg).call().await?.value;
        let expected = arg + u256_from((3, 4, 5, 6));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u256_in_enum_output().call().await?.value;
        let expected = SomeEnum::B(u256_from((1, 2, 3, 4)));

        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u256_from((2, 3, 4, 5)));

        contract_methods.u256_in_enum_input(input).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_base_type_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: returning_vec
    let response = contract_methods.u8_in_vec(10).call().await?;
    assert_eq!(response.value, (0..10).collect::<Vec<_>>());
    // ANCHOR_END: returning_vec

    let response = contract_methods.u16_in_vec(11).call().await?;
    assert_eq!(response.value, (0..11).collect::<Vec<_>>());

    let response = contract_methods.u32_in_vec(12).call().await?;
    assert_eq!(response.value, (0..12).collect::<Vec<_>>());

    let response = contract_methods.u64_in_vec(13).call().await?;
    assert_eq!(response.value, (0..13).collect::<Vec<_>>());

    let response = contract_methods.bool_in_vec().call().await?;
    assert_eq!(response.value, [true, false, true, false].to_vec());

    let response = contract_methods.b256_in_vec(13).call().await?;
    assert_eq!(response.value, vec![Bits256([2; 32]); 13]);

    Ok(())
}

#[tokio::test]
async fn test_composite_types_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let expected: Vec<[u64; 4]> = vec![[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
        let response = contract_methods.array_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    {
        let expected: Vec<Pasta> = vec![
            Pasta::Tortelini(Bimbam {
                bim: 1111,
                bam: 2222_u32,
            }),
            Pasta::Rigatoni(1987),
            Pasta::Spaghetti(true),
        ];

        let response = contract_methods.enum_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<Bimbam> = vec![
            Bimbam {
                bim: 1111,
                bam: 2222_u32,
            },
            Bimbam {
                bim: 3333,
                bam: 4444_u32,
            },
            Bimbam {
                bim: 5555,
                bam: 6666_u32,
            },
        ];
        let response = contract_methods.struct_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<(u64, u32)> = vec![(1111, 2222_u32), (3333, 4444_u32), (5555, 6666_u32)];
        let response = contract_methods.tuple_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<SizedAsciiString<4>> =
            vec!["hell".try_into()?, "ello".try_into()?, "lloh".try_into()?];
        let response = contract_methods.str_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    Ok(())
}

#[tokio::test]
async fn test_bytes_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesOutputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.return_bytes(10).call().await?;

    assert_eq!(response.value, (0..10).collect::<Vec<_>>());

    Ok(())
}

#[tokio::test]
async fn test_bytes_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesInputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesInputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: bytes_arg
        let bytes = Bytes(vec![40, 41, 42]);

        contract_methods.accept_bytes(bytes).call().await?;
        // ANCHOR_END: bytes_arg
    }
    {
        let bytes = Bytes(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![bytes.clone(), bytes.clone()],
            inner_enum: SomeEnum::Second(bytes),
        };

        contract_methods.accept_nested_bytes(wrapper).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_raw_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RawSliceContract",
            project = "e2e/sway/types/contracts/raw_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RawSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    {
        for length in 0u8..=10 {
            let response = contract_methods.return_raw_slice(length).call().await?;
            assert_eq!(response.value, (0u8..length).collect::<Vec<u8>>());
        }
    }
    {
        contract_methods
            .accept_raw_slice(RawSlice(vec![40, 41, 42]))
            .call()
            .await?;
    }
    {
        let raw_slice = RawSlice(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![raw_slice.clone(), raw_slice.clone()],
            inner_enum: SomeEnum::Second(raw_slice),
        };

        contract_methods
            .accept_nested_raw_slice(wrapper)
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_string_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StringSliceContract",
            project = "e2e/sway/types/contracts/string_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StringSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .handles_str("contract-input".try_into()?)
        .call()
        .await?;
    assert_eq!(response.value, "contract-return");

    Ok(())
}

#[tokio::test]
async fn contract_std_lib_string() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StdLibString",
            project = "e2e/sway/types/contracts/std_lib_string"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StdLibString",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.return_dynamic_string().call().await?.value;
        assert_eq!(resp, "Hello World");
    }
    {
        let _resp = contract_methods
            .accepts_dynamic_string(String::from("Hello World"))
            .call()
            .await?;
    }
    {
        // confirm encoding/decoding a string wasn't faulty and led to too high gas consumption
        let _resp = contract_methods
            .echoes_dynamic_string(String::from("Hello Fuel"))
            .with_tx_policies(TxPolicies::default().with_script_gas_limit(3600))
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_heap_type_in_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_type_in_enums"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.returns_bytes_result(true).call().await?;
        let expected = Ok(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_result(false).call().await?;
        let expected = Err(TestError::Something([255u8, 255u8, 255u8, 255u8, 255u8]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(true).call().await?;
        let expected = Ok(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(false).call().await?;
        let expected = Err(TestError::Else(7777));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(true).call().await?;
        let expected = Ok("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_str_result(true).call().await?;
        let expected = Ok("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(true).call().await?;
        let expected = Some(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_vec_option(true).call().await?;
        let expected = Some(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_string_option(true).call().await?;
        let expected = Some("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_str_option(true).call().await?;
        let expected = Some("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }

    Ok(())
}

#[tokio::test]
async fn nested_heap_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let arr = [2u8, 4, 8];
    let struct_generics = StructGenerics {
        one: Bytes(arr.to_vec()),
        two: String::from("fuel"),
        three: RawSlice(arr.to_vec()),
    };

    let enum_vec = [struct_generics.clone(), struct_generics].to_vec();
    let expected = EnumGeneric::One(enum_vec);

    let result = contract_instance
        .methods()
        .nested_heap_types()
        .call()
        .await?;

    assert_eq!(result.value, expected);

    Ok(())
}\n```

You can freely use your custom types (structs or enums) within this scope. That also means passing custom types to functions and receiving custom types from function calls.

Generics

The Fuel Rust SDK supports both generic enums and generic structs. If you're already familiar with Rust, it's your typical struct MyStruct<T> type of generics support.

For instance, your Sway contract could look like this:

contract;

use std::hash::sha256;

struct SimpleGeneric<T> {
    single_generic_param: T,
}

abi MyContract {
  fn struct_w_generic(arg1: SimpleGeneric<u64>) -> SimpleGeneric<u64>;
}

impl MyContract for Contract {
    fn struct_w_generic(arg1: SimpleGeneric<u64>) -> SimpleGeneric<u64> {
        let expected = SimpleGeneric {
            single_generic_param: 123u64,
        };

        assert(arg1.single_generic_param == expected.single_generic_param);

        expected
    }
}

Your Rust code would look like this:

```rust\nuse std::str::FromStr;

use fuels::{
    prelude::*,
    types::{Bits256, EvmAddress, Identity, SizedAsciiString, B512, U256},
};

pub fn null_contract_id() -> Bech32ContractId {
    // a bech32 contract address that decodes to [0u8;32]
    Bech32ContractId::from_str("fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2")
        .expect("is valid")
}

#[tokio::test]
async fn test_methods_typeless_argument() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/empty_arguments"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .method_with_empty_argument()
        .call()
        .await?;

    assert_eq!(response.value, 63);

    Ok(())
}

#[tokio::test]
async fn call_with_empty_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/types/contracts/call_empty_return"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let _response = contract_instance.methods().store_value(42).call().await?;
    Ok(())
}

#[tokio::test]
async fn call_with_structs() -> Result<()> {
    // Generates the bindings from the an ABI definition inline.
    // The generated bindings can be accessed through `MyContract`.
    // ANCHOR: struct_generation
    abigen!(Contract(name="MyContract",
                     abi="e2e/sway/types/contracts/complex_types_contract/out/release/complex_types_contract-abi.json"));

    // Here we can use `CounterConfig`, a struct originally
    // defined in the contract.
    let counter_config = CounterConfig {
        dummy: true,
        initial_value: 42,
    };
    // ANCHOR_END: struct_generation

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/types/contracts/complex_types_contract/out/release/complex_types_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id, wallet).methods();

    let response = contract_methods
        .initialize_counter(counter_config)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let response = contract_methods.increment_counter(10).call().await?;

    assert_eq!(52, response.value);

    Ok(())
}

#[tokio::test]
async fn abigen_different_structs_same_arg_name() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/two_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let param_one = StructOne { foo: 42 };
    let param_two = StructTwo { bar: 42 };

    let contract_methods = contract_instance.methods();
    let res_one = contract_methods.something(param_one).call().await?;

    assert_eq!(res_one.value, 43);

    let res_two = contract_methods.something_else(param_two).call().await?;

    assert_eq!(res_two.value, 41);
    Ok(())
}

#[tokio::test]
async fn nested_structs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/nested_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = AllStruct {
        some_struct: SomeStruct {
            field: 12345,
            field_2: true,
        },
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_struct().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_struct_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the argument correctly. Investigate!"
    );

    let memory_address = MemoryAddress {
        contract_id: ContractId::zeroed(),
        function_selector: 10,
        function_data: 0,
    };

    let call_data = CallData {
        memory_address,
        num_coins_to_forward: 10,
        asset_id_of_coins_to_forward: ContractId::zeroed(),
        amount_of_gas_to_forward: 5,
    };

    let actual = contract_methods
        .nested_struct_with_reserved_keyword_substring(call_data.clone())
        .call()
        .await?
        .value;

    assert_eq!(actual, call_data);
    Ok(())
}

#[tokio::test]
async fn calls_with_empty_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/complex_types_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.get_empty_struct().call().await?;

        assert_eq!(response.value, EmptyStruct {});
    }
    {
        let response = contract_methods
            .input_empty_struct(EmptyStruct {})
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_struct_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let cocktail_in_bytes: Vec<u8> = vec![
        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3,
    ];

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(2),
        glass: 3,
    };

    // as slice
    let actual: Cocktail = cocktail_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Cocktail = (&cocktail_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Cocktail = cocktail_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn test_tuples() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/tuples"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.returns_tuple((1, 2)).call().await?;

        assert_eq!(response.value, (1, 2));
    }
    {
        // Tuple with struct.
        let my_struct_tuple = (
            42,
            Person {
                name: "Jane".try_into()?,
            },
        );
        let response = contract_methods
            .returns_struct_in_tuple(my_struct_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_struct_tuple);
    }
    {
        // Tuple with enum.
        let my_enum_tuple: (u64, State) = (42, State::A);

        let response = contract_methods
            .returns_enum_in_tuple(my_enum_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // Tuple with single element
        let my_enum_tuple = (123u64,);

        let response = contract_methods
            .single_element_tuple(my_enum_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // tuple with b256
        let id = *ContractId::zeroed();
        let my_b256_u8_tuple = (Bits256(id), 10);

        let response = contract_methods
            .tuple_with_b256(my_b256_u8_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_b256_u8_tuple);
    }

    Ok(())
}

#[tokio::test]
async fn test_evm_address() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/evm_address"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        // ANCHOR: evm_address_arg
        let b256 = Bits256::from_hex_str(
            "0x1616060606060606060606060606060606060606060606060606060606060606",
        )?;
        let evm_address = EvmAddress::from(b256);

        let call_handler = contract_instance
            .methods()
            .evm_address_as_input(evm_address);
        // ANCHOR_END: evm_address_arg

        assert!(call_handler.call().await?.value);
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_literal()
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_argument(b256)
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        contract_instance
            .methods()
            .get_array([42; 2])
            .call()
            .await?
            .value,
        [42; 2]
    );
    Ok(())
}

#[tokio::test]
async fn test_arrays_with_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let persons = [
        Person {
            name: "John".try_into()?,
        },
        Person {
            name: "Jane".try_into()?,
        },
    ];

    let contract_methods = contract_instance.methods();
    let response = contract_methods.array_of_structs(persons).call().await?;

    assert_eq!("John", response.value[0].name);
    assert_eq!("Jane", response.value[1].name);

    let states = [State::A, State::B];

    let response = contract_methods
        .array_of_enums(states.clone())
        .call()
        .await?;

    assert_eq!(states[0], response.value[0]);
    assert_eq!(states[1], response.value[1]);
    Ok(())
}

#[tokio::test]
async fn str_in_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/str_in_array"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let input = ["foo", "bar", "baz"].map(|str| str.try_into().unwrap());
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .take_array_string_shuffle(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["baz", "foo", "bar"]);

    let response = contract_methods
        .take_array_string_return_single(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["foo"]);

    let response = contract_methods
        .take_array_string_return_single_element(input)
        .call()
        .await?;

    assert_eq!(response.value, "bar");
    Ok(())
}

#[tokio::test]
async fn test_enum_inside_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_inside_struct"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(11),
        glass: 333,
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .return_enum_inside_struct(11)
        .call()
        .await?;

    assert_eq!(response.value, expected);

    let enum_inside_struct = Cocktail {
        the_thing_you_mix_in: Shaker::Cosmopolitan(444),
        glass: 555,
    };

    let response = contract_methods
        .take_enum_inside_struct(enum_inside_struct)
        .call()
        .await?;

    assert_eq!(response.value, 555);
    Ok(())
}

#[tokio::test]
async fn native_types_support() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/native_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let user = User {
        weight: 10,
        address: Address::zeroed(),
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods.wrapped_address(user).call().await?;

    assert_eq!(response.value.address, Address::zeroed());

    let response = contract_methods
        .unwrapped_address(Address::zeroed())
        .call()
        .await?;

    assert_eq!(
        response.value,
        Address::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")?
    );

    Ok(())
}

#[tokio::test]
async fn enum_coding_w_variable_width_variants() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of enum encoding width, then we'll
    // probably end up mangling arg_2 and onward which will fail this test.
    let expected = BigBundle {
        arg_1: EnumThatHasABigAndSmallVariant::Small(12345),
        arg_2: 6666,
        arg_3: 7777,
        arg_4: 8888,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_big_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_big_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_coding_w_unit_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of unit enum encoding width, then
    // we'll end up mangling arg_2
    let expected = UnitBundle {
        arg_1: UnitEnum::var2,
        arg_2: u64::MAX,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_unit_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_as_input"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = MaxedOutVariantsEnum::Variant255(11);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_max_variant().call().await?.value;
    assert_eq!(expected, actual);

    let expected = StandardEnum::Two(12345);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_standard_enum().call().await?.value;
    assert_eq!(expected, actual);

    let fuelvm_judgement = contract_methods
        .check_standard_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the standard enum correctly. Investigate!"
    );

    let expected = UnitEnum::Two;
    let actual = contract_methods.get_unit_enum().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the unit enum correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_enum_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let shaker_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];

    let expected = Shaker::Mojito(2);

    // as slice
    let actual: Shaker = shaker_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Shaker = (&shaker_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Shaker = shaker_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn type_inside_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/type_inside_enum"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // String inside enum
    let enum_string = SomeEnum::SomeStr("asdf".try_into()?);
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .str_inside_enum(enum_string.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_string);

    // Array inside enum
    let enum_array = SomeEnum::SomeArr([1, 2, 3, 4]);
    let response = contract_methods
        .arr_inside_enum(enum_array.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_array);

    // Struct inside enum
    let response = contract_methods
        .return_struct_inside_enum(11)
        .call()
        .await?;
    let expected = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 11 });
    assert_eq!(response.value, expected);

    let struct_inside_enum = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 66 });
    let response = contract_methods
        .take_struct_inside_enum(struct_inside_enum)
        .call()
        .await?;
    assert_eq!(response.value, 8888);

    // Enum inside enum
    let expected_enum = EnumLevel3::El2(EnumLevel2::El1(EnumLevel1::Num(42)));
    let response = contract_methods.get_nested_enum().call().await?;
    assert_eq!(response.value, expected_enum);

    let response = contract_methods
        .check_nested_enum_integrity(expected_enum)
        .call()
        .await?;
    assert!(
        response.value,
        "The FuelVM deems that we've not encoded the nested enum correctly. Investigate!"
    );

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_some_address = Some(expected_address);
    let response = contract_methods.get_some_address().call().await?;

    assert_eq!(response.value, expected_some_address);

    let expected_some_u64 = Some(10);
    let response = contract_methods.get_some_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_some_struct().call().await?;
    assert_eq!(response.value, Some(s.clone()));

    let response = contract_methods.get_some_enum().call().await?;
    assert_eq!(response.value, Some(e.clone()));

    let response = contract_methods.get_some_tuple().call().await?;
    assert_eq!(response.value, Some((s.clone(), e.clone())));

    let expected_none = None;
    let response = contract_methods.get_none().call().await?;

    assert_eq!(response.value, expected_none);

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_u64 = Some(36);
    let response = contract_methods
        .input_primitive(expected_u64)
        .call()
        .await?;

    assert!(response.value);

    let expected_struct = Some(s);
    let response = contract_methods
        .input_struct(expected_struct)
        .call()
        .await?;

    assert!(response.value);

    let expected_enum = Some(e);
    let response = contract_methods.input_enum(expected_enum).call().await?;

    assert!(response.value);

    let expected_none = None;
    let response = contract_methods.input_none(expected_none).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods.get_ok_address().call().await?;

    assert_eq!(response.value, expected_ok_address);

    let expected_some_u64 = Ok(10);
    let response = contract_methods.get_ok_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_ok_struct().call().await?;
    assert_eq!(response.value, Ok(s.clone()));

    let response = contract_methods.get_ok_enum().call().await?;
    assert_eq!(response.value, Ok(e.clone()));

    let response = contract_methods.get_ok_tuple().call().await?;
    assert_eq!(response.value, Ok((s, e)));

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.get_error().call().await?;

    assert_eq!(response.value, expected_error);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods
        .input_ok(expected_ok_address)
        .call()
        .await?;

    assert!(response.value);

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.input_error(expected_error).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_identity_address().call().await?;
    assert_eq!(response.value, Identity::Address(expected_address));

    let response = contract_methods.get_identity_contract_id().call().await?;
    assert_eq!(response.value, Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_struct_with_identity().call().await?;
    assert_eq!(response.value, s.clone());

    let response = contract_methods.get_enum_with_identity().call().await?;
    assert_eq!(response.value, e.clone());

    let response = contract_methods.get_identity_tuple().call().await?;
    assert_eq!(response.value, (s, e));

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods
        .input_identity(Identity::Address(expected_address))
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods
        .input_struct_with_identity(s)
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods.input_enum_with_identity(e).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_with_two_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    {
        let response = contract_instance
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }
    {
        let response = contract_instance2
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn generics_test() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/generics"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: generic
        // simple struct with a single generic param
        let arg1 = SimpleGeneric {
            single_generic_param: 123u64,
        };

        let result = contract_methods
            .struct_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
        // ANCHOR_END: generic
    }
    {
        // struct that delegates the generic param internally
        let arg1 = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "abc".try_into()?,
            },
        };

        let result = contract_methods
            .struct_delegating_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in an array
        let arg1 = StructWArrayGeneric { a: [1u32, 2u32] };

        let result = contract_methods
            .struct_w_generic_in_array(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has a generic struct in an array
        let inner = [
            StructWTwoGenerics {
                a: Bits256([1u8; 32]),
                b: 1,
            },
            StructWTwoGenerics {
                a: Bits256([2u8; 32]),
                b: 2,
            },
            StructWTwoGenerics {
                a: Bits256([3u8; 32]),
                b: 3,
            },
        ];
        let arg1 = StructWArrWGenericStruct { a: inner };

        let result = contract_methods
            .array_with_generic_struct(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in a tuple
        let arg1 = StructWTupleGeneric { a: (1, 2) };

        let result = contract_methods
            .struct_w_generic_in_tuple(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // enum with generic in variant
        let arg1 = EnumWGeneric::B(10);
        let result = contract_methods
            .enum_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        contract_methods
            .unused_generic_args(StructUnusedGeneric::new(15), EnumUnusedGeneric::One(15))
            .call()
            .await?;

        let (the_struct, the_enum) = contract_methods
            .used_and_unused_generic_args(
                StructUsedAndUnusedGenericParams::new(10u8),
                EnumUsedAndUnusedGenericParams::Two(11u8),
            )
            .call()
            .await?
            .value;

        assert_eq!(the_struct.field, 12u8);
        if let EnumUsedAndUnusedGenericParams::Two(val) = the_enum {
            assert_eq!(val, 13)
        } else {
            panic!("Expected the variant EnumUsedAndUnusedGenericParams::Two");
        }
    }
    {
        // complex case
        let pass_through = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "ab".try_into()?,
            },
        };
        let w_arr_generic = StructWArrayGeneric {
            a: [pass_through.clone(), pass_through],
        };

        let arg1 = MegaExample {
            a: ([Bits256([0; 32]), Bits256([0; 32])], "ab".try_into()?),
            b: vec![(
                [EnumWGeneric::B(StructWTupleGeneric {
                    a: (w_arr_generic.clone(), w_arr_generic),
                })],
                10u32,
            )],
        };
        contract_methods.complex_test(arg1.clone()).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_vectors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/vectors"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let methods = contract_instance.methods();

    {
        // vec of u32s
        let arg = vec![0, 1, 2];
        methods.u32_vec(arg).call().await?;
    }
    {
        // vec of vecs of u32s
        let arg = vec![vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_vec(arg.clone()).call().await?;
    }
    {
        // vec of structs
        // ANCHOR: passing_in_vec
        let arg = vec![SomeStruct { a: 0 }, SomeStruct { a: 1 }];
        methods.struct_in_vec(arg.clone()).call().await?;
        // ANCHOR_END: passing_in_vec
    }
    {
        // vec in struct
        let arg = SomeStruct { a: vec![0, 1, 2] };
        methods.vec_in_struct(arg.clone()).call().await?;
    }
    {
        // array in vec
        let arg = vec![[0u64, 1u64], [0u64, 1u64]];
        methods.array_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in array
        let arg = [vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_array(arg.clone()).call().await?;
    }
    {
        // vec in enum
        let arg = SomeEnum::a(vec![0, 1, 2]);
        methods.vec_in_enum(arg.clone()).call().await?;
    }
    {
        // enum in vec
        let arg = vec![SomeEnum::a(0), SomeEnum::a(1)];
        methods.enum_in_vec(arg.clone()).call().await?;
    }
    {
        // tuple in vec
        let arg = vec![(0, 0), (1, 1)];
        methods.tuple_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in tuple
        let arg = (vec![0, 1, 2], vec![0, 1, 2]);
        methods.vec_in_tuple(arg.clone()).call().await?;
    }
    {
        // vec in a vec in a struct in a vec
        let arg = vec![
            SomeStruct {
                a: vec![vec![0, 1, 2], vec![3, 4, 5]],
            },
            SomeStruct {
                a: vec![vec![6, 7, 8], vec![9, 10, 11]],
            },
        ];
        methods
            .vec_in_a_vec_in_a_struct_in_a_vec(arg.clone())
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_b256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        Bits256([2; 32]),
        contract_instance
            .methods()
            .b256_as_output()
            .call()
            .await?
            .value
    );

    {
        // ANCHOR: 256_arg
        let b256 = Bits256([1; 32]);

        let call_handler = contract_instance.methods().b256_as_input(b256);
        // ANCHOR_END: 256_arg

        assert!(call_handler.call().await?.value);
    }

    Ok(())
}

#[tokio::test]
async fn test_b512() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b512"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: b512_example
    let hi_bits = Bits256::from_hex_str(
        "0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c",
    )?;
    let lo_bits = Bits256::from_hex_str(
        "0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
    )?;
    let b512 = B512::from((hi_bits, lo_bits));
    // ANCHOR_END: b512_example

    assert_eq!(b512, contract_methods.b512_as_output().call().await?.value);

    {
        let lo_bits2 = Bits256::from_hex_str(
            "0x54ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
        )?;
        let b512 = B512::from((hi_bits, lo_bits2));

        assert!(contract_methods.b512_as_input(b512).call().await?.value);
    }

    Ok(())
}

fn u128_from(parts: (u64, u64)) -> u128 {
    let bytes: [u8; 16] = [parts.0.to_be_bytes(), parts.1.to_be_bytes()]
        .concat()
        .try_into()
        .unwrap();
    u128::from_be_bytes(bytes)
}

#[tokio::test]
async fn test_u128() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u128"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u128_from((1, 2));

        let actual = contract_methods.u128_sum_and_ret(arg).call().await?.value;

        let expected = arg + u128_from((3, 4));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u128_in_enum_output().call().await?.value;

        let expected = SomeEnum::B(u128_from((4, 4)));
        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u128_from((3, 3)));

        contract_methods.u128_in_enum_input(input).call().await?;
    }

    Ok(())
}

fn u256_from(parts: (u64, u64, u64, u64)) -> U256 {
    let bytes: [u8; 32] = [
        parts.0.to_be_bytes(),
        parts.1.to_be_bytes(),
        parts.2.to_be_bytes(),
        parts.3.to_be_bytes(),
    ]
    .concat()
    .try_into()
    .unwrap();
    U256::from(bytes)
}

#[tokio::test]
async fn test_u256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u256_from((1, 2, 3, 4));
        let actual = contract_methods.u256_sum_and_ret(arg).call().await?.value;
        let expected = arg + u256_from((3, 4, 5, 6));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u256_in_enum_output().call().await?.value;
        let expected = SomeEnum::B(u256_from((1, 2, 3, 4)));

        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u256_from((2, 3, 4, 5)));

        contract_methods.u256_in_enum_input(input).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_base_type_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: returning_vec
    let response = contract_methods.u8_in_vec(10).call().await?;
    assert_eq!(response.value, (0..10).collect::<Vec<_>>());
    // ANCHOR_END: returning_vec

    let response = contract_methods.u16_in_vec(11).call().await?;
    assert_eq!(response.value, (0..11).collect::<Vec<_>>());

    let response = contract_methods.u32_in_vec(12).call().await?;
    assert_eq!(response.value, (0..12).collect::<Vec<_>>());

    let response = contract_methods.u64_in_vec(13).call().await?;
    assert_eq!(response.value, (0..13).collect::<Vec<_>>());

    let response = contract_methods.bool_in_vec().call().await?;
    assert_eq!(response.value, [true, false, true, false].to_vec());

    let response = contract_methods.b256_in_vec(13).call().await?;
    assert_eq!(response.value, vec![Bits256([2; 32]); 13]);

    Ok(())
}

#[tokio::test]
async fn test_composite_types_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let expected: Vec<[u64; 4]> = vec![[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
        let response = contract_methods.array_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    {
        let expected: Vec<Pasta> = vec![
            Pasta::Tortelini(Bimbam {
                bim: 1111,
                bam: 2222_u32,
            }),
            Pasta::Rigatoni(1987),
            Pasta::Spaghetti(true),
        ];

        let response = contract_methods.enum_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<Bimbam> = vec![
            Bimbam {
                bim: 1111,
                bam: 2222_u32,
            },
            Bimbam {
                bim: 3333,
                bam: 4444_u32,
            },
            Bimbam {
                bim: 5555,
                bam: 6666_u32,
            },
        ];
        let response = contract_methods.struct_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<(u64, u32)> = vec![(1111, 2222_u32), (3333, 4444_u32), (5555, 6666_u32)];
        let response = contract_methods.tuple_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<SizedAsciiString<4>> =
            vec!["hell".try_into()?, "ello".try_into()?, "lloh".try_into()?];
        let response = contract_methods.str_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    Ok(())
}

#[tokio::test]
async fn test_bytes_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesOutputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.return_bytes(10).call().await?;

    assert_eq!(response.value, (0..10).collect::<Vec<_>>());

    Ok(())
}

#[tokio::test]
async fn test_bytes_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesInputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesInputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: bytes_arg
        let bytes = Bytes(vec![40, 41, 42]);

        contract_methods.accept_bytes(bytes).call().await?;
        // ANCHOR_END: bytes_arg
    }
    {
        let bytes = Bytes(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![bytes.clone(), bytes.clone()],
            inner_enum: SomeEnum::Second(bytes),
        };

        contract_methods.accept_nested_bytes(wrapper).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_raw_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RawSliceContract",
            project = "e2e/sway/types/contracts/raw_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RawSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    {
        for length in 0u8..=10 {
            let response = contract_methods.return_raw_slice(length).call().await?;
            assert_eq!(response.value, (0u8..length).collect::<Vec<u8>>());
        }
    }
    {
        contract_methods
            .accept_raw_slice(RawSlice(vec![40, 41, 42]))
            .call()
            .await?;
    }
    {
        let raw_slice = RawSlice(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![raw_slice.clone(), raw_slice.clone()],
            inner_enum: SomeEnum::Second(raw_slice),
        };

        contract_methods
            .accept_nested_raw_slice(wrapper)
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_string_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StringSliceContract",
            project = "e2e/sway/types/contracts/string_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StringSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .handles_str("contract-input".try_into()?)
        .call()
        .await?;
    assert_eq!(response.value, "contract-return");

    Ok(())
}

#[tokio::test]
async fn contract_std_lib_string() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StdLibString",
            project = "e2e/sway/types/contracts/std_lib_string"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StdLibString",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.return_dynamic_string().call().await?.value;
        assert_eq!(resp, "Hello World");
    }
    {
        let _resp = contract_methods
            .accepts_dynamic_string(String::from("Hello World"))
            .call()
            .await?;
    }
    {
        // confirm encoding/decoding a string wasn't faulty and led to too high gas consumption
        let _resp = contract_methods
            .echoes_dynamic_string(String::from("Hello Fuel"))
            .with_tx_policies(TxPolicies::default().with_script_gas_limit(3600))
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_heap_type_in_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_type_in_enums"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.returns_bytes_result(true).call().await?;
        let expected = Ok(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_result(false).call().await?;
        let expected = Err(TestError::Something([255u8, 255u8, 255u8, 255u8, 255u8]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(true).call().await?;
        let expected = Ok(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(false).call().await?;
        let expected = Err(TestError::Else(7777));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(true).call().await?;
        let expected = Ok("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_str_result(true).call().await?;
        let expected = Ok("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(true).call().await?;
        let expected = Some(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_vec_option(true).call().await?;
        let expected = Some(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_string_option(true).call().await?;
        let expected = Some("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_str_option(true).call().await?;
        let expected = Some("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }

    Ok(())
}

#[tokio::test]
async fn nested_heap_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let arr = [2u8, 4, 8];
    let struct_generics = StructGenerics {
        one: Bytes(arr.to_vec()),
        two: String::from("fuel"),
        three: RawSlice(arr.to_vec()),
    };

    let enum_vec = [struct_generics.clone(), struct_generics].to_vec();
    let expected = EnumGeneric::One(enum_vec);

    let result = contract_instance
        .methods()
        .nested_heap_types()
        .call()
        .await?;

    assert_eq!(result.value, expected);

    Ok(())
}\n```

Unused generic type parameters

Sway supports unused generic type parameters when declaring structs/enums:

struct SomeStruct<T, K> {
  field: u64
}

enum SomeEnum<T, K> {
  One: u64
}

If you tried the same in Rust you'd get complaints that T and K must be used or removed. When generating Rust bindings for such types we make use of the PhantomData type. The generated bindings for the above example would look something like this:

struct SomeStruct<T, K> {
   pub field: u64,
   pub _unused_generic_0: PhantomData<T>
   pub _unused_generic_1: PhantomData<K>
}

enum SomeEnum<T, K> {
  One(u64),
  IgnoreMe(PhantomData<T>, PhantomData<K>)
}

To lessen the impact to developer experience you may use the new method to initialize a structure without bothering with the PhantomDatas.:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

If your struct doesn't have any fields we'll also derive Default. As for enums all PhantomDatas are placed inside a new variant called IgnoreMe which you'll need to ignore in your matches:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

String

The Rust SDK represents Fuel's Strings as SizedAsciiString<LEN>, where the generic parameter LEN is the length of a given string. This abstraction is necessary because all strings in Fuel and Sway are statically-sized, i.e., you must know the size of the string beforehand.

Here's how you can create a simple string using SizedAsciiString:

```rust\nuse std::fmt::{Debug, Display, Formatter};

use serde::{Deserialize, Serialize};

use crate::types::errors::{error, Error, Result};

// To be used when interacting with contracts which have string slices in their ABI.
// The FuelVM strings only support ascii characters.
#[derive(Debug, PartialEq, Clone, Eq)]
pub struct AsciiString {
    data: String,
}

impl AsciiString {
    pub fn new(data: String) -> Result<Self> {
        if !data.is_ascii() {
            return Err(error!(Other,
                "`AsciiString` must be constructed from a string containing only ascii encodable characters. Got: `{data}`"
            ));
        }
        Ok(Self { data })
    }

    pub fn to_trimmed_str(&self) -> &str {
        self.data.trim()
    }
    pub fn to_left_trimmed_str(&self) -> &str {
        self.data.trim_start()
    }
    pub fn to_right_trimmed_str(&self) -> &str {
        self.data.trim_end()
    }
}

impl TryFrom<&str> for AsciiString {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self> {
        Self::new(value.to_owned())
    }
}

impl TryFrom<String> for AsciiString {
    type Error = Error;

    fn try_from(value: String) -> Result<Self> {
        Self::new(value)
    }
}

impl From<AsciiString> for String {
    fn from(ascii_str: AsciiString) -> Self {
        ascii_str.data
    }
}

impl<const LEN: usize> AsRef<str> for SizedAsciiString<LEN> {
    fn as_ref(&self) -> &str {
        &self.data
    }
}

impl Display for AsciiString {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.data)
    }
}

impl PartialEq<&str> for AsciiString {
    fn eq(&self, other: &&str) -> bool {
        self.data == *other
    }
}
impl PartialEq<AsciiString> for &str {
    fn eq(&self, other: &AsciiString) -> bool {
        *self == other.data
    }
}

// To be used when interacting with contracts which have strings in their ABI.
// The length of a string is part of its type -- i.e. str[2] is a
// different type from str[3]. The FuelVM strings only support ascii characters.
#[derive(Debug, PartialEq, Clone, Eq, Hash, Default)]
pub struct SizedAsciiString<const LEN: usize> {
    data: String,
}

impl<const LEN: usize> SizedAsciiString<LEN> {
    pub fn new(data: String) -> Result<Self> {
        if !data.is_ascii() {
            return Err(error!(Other,
                "`SizedAsciiString` must be constructed from a `String` containing only ascii encodable characters. Got: `{data}`"
            ));
        }
        if data.len() != LEN {
            return Err(error!(Other,
                "`SizedAsciiString<{LEN}>` must be constructed from a `String` of length {LEN}. Got: `{data}`"
            ));
        }
        Ok(Self { data })
    }

    pub fn to_trimmed_str(&self) -> &str {
        self.data.trim()
    }
    pub fn to_left_trimmed_str(&self) -> &str {
        self.data.trim_start()
    }
    pub fn to_right_trimmed_str(&self) -> &str {
        self.data.trim_end()
    }

    /// Pad `data` string with whitespace characters on the right to fit into the `SizedAsciiString`
    pub fn new_with_right_whitespace_padding(data: String) -> Result<Self> {
        if data.len() > LEN {
            return Err(error!(
                Other,
                "`SizedAsciiString<{LEN}>` cannot be constructed from a string of size {}",
                data.len()
            ));
        }

        Ok(Self {
            data: format!("{:LEN$}", data),
        })
    }
}

impl<const LEN: usize> TryFrom<&str> for SizedAsciiString<LEN> {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self> {
        Self::new(value.to_owned())
    }
}

impl<const LEN: usize> TryFrom<String> for SizedAsciiString<LEN> {
    type Error = Error;

    fn try_from(value: String) -> Result<Self> {
        Self::new(value)
    }
}

impl<const LEN: usize> From<SizedAsciiString<LEN>> for String {
    fn from(sized_ascii_str: SizedAsciiString<LEN>) -> Self {
        sized_ascii_str.data
    }
}

impl<const LEN: usize> Display for SizedAsciiString<LEN> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.data)
    }
}

impl<const LEN: usize> PartialEq<&str> for SizedAsciiString<LEN> {
    fn eq(&self, other: &&str) -> bool {
        self.data == *other
    }
}

impl<const LEN: usize> PartialEq<SizedAsciiString<LEN>> for &str {
    fn eq(&self, other: &SizedAsciiString<LEN>) -> bool {
        *self == other.data
    }
}

impl<const LEN: usize> Serialize for SizedAsciiString<LEN> {
    fn serialize<S: serde::Serializer>(
        &self,
        serializer: S,
    ) -> core::result::Result<S::Ok, S::Error> {
        self.data.serialize(serializer)
    }
}

impl<'de, const LEN: usize> Deserialize<'de> for SizedAsciiString<LEN> {
    fn deserialize<D: serde::Deserializer<'de>>(
        deserializer: D,
    ) -> core::result::Result<Self, D::Error> {
        let data = String::deserialize(deserializer)?;
        Self::new(data).map_err(serde::de::Error::custom)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn accepts_ascii_of_correct_length() {
        // ANCHOR: string_simple_example
        let ascii_data = "abc".to_string();

        SizedAsciiString::<3>::new(ascii_data)
            .expect("should have succeeded since we gave ascii data of correct length!");
        // ANCHOR_END: string_simple_example
    }

    #[test]
    fn refuses_non_ascii() {
        let ascii_data = "ab©".to_string();

        let err = SizedAsciiString::<3>::new(ascii_data)
            .expect_err("should not have succeeded since we gave non ascii data");

        let expected_reason = "`SizedAsciiString` must be constructed from a `String` containing only ascii encodable characters. Got: ";
        assert!(matches!(err, Error::Other(reason) if reason.starts_with(expected_reason)));
    }

    #[test]
    fn refuses_invalid_len() {
        let ascii_data = "abcd".to_string();

        let err = SizedAsciiString::<3>::new(ascii_data)
            .expect_err("should not have succeeded since we gave data of wrong length");

        let expected_reason =
            "`SizedAsciiString<3>` must be constructed from a `String` of length 3. Got: `abcd`";
        assert!(matches!(err, Error::Other(reason) if reason.starts_with(expected_reason)));
    }

    // ANCHOR: conversion
    #[test]
    fn can_be_constructed_from_str_ref() {
        let _: SizedAsciiString<3> = "abc".try_into().expect("should have succeeded");
    }

    #[test]
    fn can_be_constructed_from_string() {
        let _: SizedAsciiString<3> = "abc".to_string().try_into().expect("should have succeeded");
    }

    #[test]
    fn can_be_converted_into_string() {
        let sized_str = SizedAsciiString::<3>::new("abc".to_string()).unwrap();

        let str: String = sized_str.into();

        assert_eq!(str, "abc");
    }
    // ANCHOR_END: conversion

    #[test]
    fn can_be_printed() {
        let sized_str = SizedAsciiString::<3>::new("abc".to_string()).unwrap();

        assert_eq!(sized_str.to_string(), "abc");
    }

    #[test]
    fn can_be_compared_w_str_ref() {
        let sized_str = SizedAsciiString::<3>::new("abc".to_string()).unwrap();

        assert_eq!(sized_str, "abc");
        // and vice-versa
        assert_eq!("abc", sized_str);
    }

    #[test]
    fn trim() -> Result<()> {
        // Using single whitespaces
        let untrimmed = SizedAsciiString::<9>::new(" est abc ".to_string())?;
        assert_eq!("est abc ", untrimmed.to_left_trimmed_str());
        assert_eq!(" est abc", untrimmed.to_right_trimmed_str());
        assert_eq!("est abc", untrimmed.to_trimmed_str());

        let padded = // adds 6 whitespaces
            SizedAsciiString::<12>::new_with_right_whitespace_padding("victor".to_string())?;
        assert_eq!("victor      ", padded);

        Ok(())
    }

    #[test]
    fn test_can_serialize_sized_ascii() {
        let sized_str = SizedAsciiString::<3>::new("abc".to_string()).unwrap();

        let serialized = serde_json::to_string(&sized_str).unwrap();
        assert_eq!(serialized, "\"abc\"");
    }

    #[test]
    fn test_can_deserialize_sized_ascii() {
        let serialized = "\"abc\"";

        let deserialized: SizedAsciiString<3> = serde_json::from_str(serialized).unwrap();
        assert_eq!(
            deserialized,
            SizedAsciiString::<3>::new("abc".to_string()).unwrap()
        );
    }

    #[test]
    fn test_can_convert_sized_ascii_to_bytes() {
        let sized_str = SizedAsciiString::<3>::new("abc".to_string()).unwrap();

        let bytes: &[u8] = sized_str.as_ref().as_bytes();
        assert_eq!(bytes, &[97, 98, 99]);
    }
}\n```

To make working with SizedAsciiStrings easier, you can use try_into() to convert from Rust's String to SizedAsciiString, and you can use into() to convert from SizedAsciiString to Rust's String. Here are a few examples:

```rust\nuse std::fmt::{Debug, Display, Formatter};

use serde::{Deserialize, Serialize};

use crate::types::errors::{error, Error, Result};

// To be used when interacting with contracts which have string slices in their ABI.
// The FuelVM strings only support ascii characters.
#[derive(Debug, PartialEq, Clone, Eq)]
pub struct AsciiString {
    data: String,
}

impl AsciiString {
    pub fn new(data: String) -> Result<Self> {
        if !data.is_ascii() {
            return Err(error!(Other,
                "`AsciiString` must be constructed from a string containing only ascii encodable characters. Got: `{data}`"
            ));
        }
        Ok(Self { data })
    }

    pub fn to_trimmed_str(&self) -> &str {
        self.data.trim()
    }
    pub fn to_left_trimmed_str(&self) -> &str {
        self.data.trim_start()
    }
    pub fn to_right_trimmed_str(&self) -> &str {
        self.data.trim_end()
    }
}

impl TryFrom<&str> for AsciiString {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self> {
        Self::new(value.to_owned())
    }
}

impl TryFrom<String> for AsciiString {
    type Error = Error;

    fn try_from(value: String) -> Result<Self> {
        Self::new(value)
    }
}

impl From<AsciiString> for String {
    fn from(ascii_str: AsciiString) -> Self {
        ascii_str.data
    }
}

impl<const LEN: usize> AsRef<str> for SizedAsciiString<LEN> {
    fn as_ref(&self) -> &str {
        &self.data
    }
}

impl Display for AsciiString {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.data)
    }
}

impl PartialEq<&str> for AsciiString {
    fn eq(&self, other: &&str) -> bool {
        self.data == *other
    }
}
impl PartialEq<AsciiString> for &str {
    fn eq(&self, other: &AsciiString) -> bool {
        *self == other.data
    }
}

// To be used when interacting with contracts which have strings in their ABI.
// The length of a string is part of its type -- i.e. str[2] is a
// different type from str[3]. The FuelVM strings only support ascii characters.
#[derive(Debug, PartialEq, Clone, Eq, Hash, Default)]
pub struct SizedAsciiString<const LEN: usize> {
    data: String,
}

impl<const LEN: usize> SizedAsciiString<LEN> {
    pub fn new(data: String) -> Result<Self> {
        if !data.is_ascii() {
            return Err(error!(Other,
                "`SizedAsciiString` must be constructed from a `String` containing only ascii encodable characters. Got: `{data}`"
            ));
        }
        if data.len() != LEN {
            return Err(error!(Other,
                "`SizedAsciiString<{LEN}>` must be constructed from a `String` of length {LEN}. Got: `{data}`"
            ));
        }
        Ok(Self { data })
    }

    pub fn to_trimmed_str(&self) -> &str {
        self.data.trim()
    }
    pub fn to_left_trimmed_str(&self) -> &str {
        self.data.trim_start()
    }
    pub fn to_right_trimmed_str(&self) -> &str {
        self.data.trim_end()
    }

    /// Pad `data` string with whitespace characters on the right to fit into the `SizedAsciiString`
    pub fn new_with_right_whitespace_padding(data: String) -> Result<Self> {
        if data.len() > LEN {
            return Err(error!(
                Other,
                "`SizedAsciiString<{LEN}>` cannot be constructed from a string of size {}",
                data.len()
            ));
        }

        Ok(Self {
            data: format!("{:LEN$}", data),
        })
    }
}

impl<const LEN: usize> TryFrom<&str> for SizedAsciiString<LEN> {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self> {
        Self::new(value.to_owned())
    }
}

impl<const LEN: usize> TryFrom<String> for SizedAsciiString<LEN> {
    type Error = Error;

    fn try_from(value: String) -> Result<Self> {
        Self::new(value)
    }
}

impl<const LEN: usize> From<SizedAsciiString<LEN>> for String {
    fn from(sized_ascii_str: SizedAsciiString<LEN>) -> Self {
        sized_ascii_str.data
    }
}

impl<const LEN: usize> Display for SizedAsciiString<LEN> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.data)
    }
}

impl<const LEN: usize> PartialEq<&str> for SizedAsciiString<LEN> {
    fn eq(&self, other: &&str) -> bool {
        self.data == *other
    }
}

impl<const LEN: usize> PartialEq<SizedAsciiString<LEN>> for &str {
    fn eq(&self, other: &SizedAsciiString<LEN>) -> bool {
        *self == other.data
    }
}

impl<const LEN: usize> Serialize for SizedAsciiString<LEN> {
    fn serialize<S: serde::Serializer>(
        &self,
        serializer: S,
    ) -> core::result::Result<S::Ok, S::Error> {
        self.data.serialize(serializer)
    }
}

impl<'de, const LEN: usize> Deserialize<'de> for SizedAsciiString<LEN> {
    fn deserialize<D: serde::Deserializer<'de>>(
        deserializer: D,
    ) -> core::result::Result<Self, D::Error> {
        let data = String::deserialize(deserializer)?;
        Self::new(data).map_err(serde::de::Error::custom)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn accepts_ascii_of_correct_length() {
        // ANCHOR: string_simple_example
        let ascii_data = "abc".to_string();

        SizedAsciiString::<3>::new(ascii_data)
            .expect("should have succeeded since we gave ascii data of correct length!");
        // ANCHOR_END: string_simple_example
    }

    #[test]
    fn refuses_non_ascii() {
        let ascii_data = "ab©".to_string();

        let err = SizedAsciiString::<3>::new(ascii_data)
            .expect_err("should not have succeeded since we gave non ascii data");

        let expected_reason = "`SizedAsciiString` must be constructed from a `String` containing only ascii encodable characters. Got: ";
        assert!(matches!(err, Error::Other(reason) if reason.starts_with(expected_reason)));
    }

    #[test]
    fn refuses_invalid_len() {
        let ascii_data = "abcd".to_string();

        let err = SizedAsciiString::<3>::new(ascii_data)
            .expect_err("should not have succeeded since we gave data of wrong length");

        let expected_reason =
            "`SizedAsciiString<3>` must be constructed from a `String` of length 3. Got: `abcd`";
        assert!(matches!(err, Error::Other(reason) if reason.starts_with(expected_reason)));
    }

    // ANCHOR: conversion
    #[test]
    fn can_be_constructed_from_str_ref() {
        let _: SizedAsciiString<3> = "abc".try_into().expect("should have succeeded");
    }

    #[test]
    fn can_be_constructed_from_string() {
        let _: SizedAsciiString<3> = "abc".to_string().try_into().expect("should have succeeded");
    }

    #[test]
    fn can_be_converted_into_string() {
        let sized_str = SizedAsciiString::<3>::new("abc".to_string()).unwrap();

        let str: String = sized_str.into();

        assert_eq!(str, "abc");
    }
    // ANCHOR_END: conversion

    #[test]
    fn can_be_printed() {
        let sized_str = SizedAsciiString::<3>::new("abc".to_string()).unwrap();

        assert_eq!(sized_str.to_string(), "abc");
    }

    #[test]
    fn can_be_compared_w_str_ref() {
        let sized_str = SizedAsciiString::<3>::new("abc".to_string()).unwrap();

        assert_eq!(sized_str, "abc");
        // and vice-versa
        assert_eq!("abc", sized_str);
    }

    #[test]
    fn trim() -> Result<()> {
        // Using single whitespaces
        let untrimmed = SizedAsciiString::<9>::new(" est abc ".to_string())?;
        assert_eq!("est abc ", untrimmed.to_left_trimmed_str());
        assert_eq!(" est abc", untrimmed.to_right_trimmed_str());
        assert_eq!("est abc", untrimmed.to_trimmed_str());

        let padded = // adds 6 whitespaces
            SizedAsciiString::<12>::new_with_right_whitespace_padding("victor".to_string())?;
        assert_eq!("victor      ", padded);

        Ok(())
    }

    #[test]
    fn test_can_serialize_sized_ascii() {
        let sized_str = SizedAsciiString::<3>::new("abc".to_string()).unwrap();

        let serialized = serde_json::to_string(&sized_str).unwrap();
        assert_eq!(serialized, "\"abc\"");
    }

    #[test]
    fn test_can_deserialize_sized_ascii() {
        let serialized = "\"abc\"";

        let deserialized: SizedAsciiString<3> = serde_json::from_str(serialized).unwrap();
        assert_eq!(
            deserialized,
            SizedAsciiString::<3>::new("abc".to_string()).unwrap()
        );
    }

    #[test]
    fn test_can_convert_sized_ascii_to_bytes() {
        let sized_str = SizedAsciiString::<3>::new("abc".to_string()).unwrap();

        let bytes: &[u8] = sized_str.as_ref().as_bytes();
        assert_eq!(bytes, &[97, 98, 99]);
    }
}\n```

If your contract's method takes and returns, for instance, a Sway's str[23]. When using the SDK, this method will take and return a SizedAsciiString<23>.

Bits256

In Fuel, a type called b256 represents hashes and holds a 256-bit value. The Rust SDK represents b256 as Bits256(value) where value is a [u8; 32]. If your contract method takes a b256 as input, you must pass a Bits256([u8; 32]) when calling it from the SDK.

Here's an example:

```rust\nuse std::str::FromStr;

use fuels::{
    prelude::*,
    types::{Bits256, EvmAddress, Identity, SizedAsciiString, B512, U256},
};

pub fn null_contract_id() -> Bech32ContractId {
    // a bech32 contract address that decodes to [0u8;32]
    Bech32ContractId::from_str("fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2")
        .expect("is valid")
}

#[tokio::test]
async fn test_methods_typeless_argument() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/empty_arguments"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .method_with_empty_argument()
        .call()
        .await?;

    assert_eq!(response.value, 63);

    Ok(())
}

#[tokio::test]
async fn call_with_empty_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/types/contracts/call_empty_return"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let _response = contract_instance.methods().store_value(42).call().await?;
    Ok(())
}

#[tokio::test]
async fn call_with_structs() -> Result<()> {
    // Generates the bindings from the an ABI definition inline.
    // The generated bindings can be accessed through `MyContract`.
    // ANCHOR: struct_generation
    abigen!(Contract(name="MyContract",
                     abi="e2e/sway/types/contracts/complex_types_contract/out/release/complex_types_contract-abi.json"));

    // Here we can use `CounterConfig`, a struct originally
    // defined in the contract.
    let counter_config = CounterConfig {
        dummy: true,
        initial_value: 42,
    };
    // ANCHOR_END: struct_generation

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/types/contracts/complex_types_contract/out/release/complex_types_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id, wallet).methods();

    let response = contract_methods
        .initialize_counter(counter_config)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let response = contract_methods.increment_counter(10).call().await?;

    assert_eq!(52, response.value);

    Ok(())
}

#[tokio::test]
async fn abigen_different_structs_same_arg_name() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/two_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let param_one = StructOne { foo: 42 };
    let param_two = StructTwo { bar: 42 };

    let contract_methods = contract_instance.methods();
    let res_one = contract_methods.something(param_one).call().await?;

    assert_eq!(res_one.value, 43);

    let res_two = contract_methods.something_else(param_two).call().await?;

    assert_eq!(res_two.value, 41);
    Ok(())
}

#[tokio::test]
async fn nested_structs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/nested_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = AllStruct {
        some_struct: SomeStruct {
            field: 12345,
            field_2: true,
        },
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_struct().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_struct_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the argument correctly. Investigate!"
    );

    let memory_address = MemoryAddress {
        contract_id: ContractId::zeroed(),
        function_selector: 10,
        function_data: 0,
    };

    let call_data = CallData {
        memory_address,
        num_coins_to_forward: 10,
        asset_id_of_coins_to_forward: ContractId::zeroed(),
        amount_of_gas_to_forward: 5,
    };

    let actual = contract_methods
        .nested_struct_with_reserved_keyword_substring(call_data.clone())
        .call()
        .await?
        .value;

    assert_eq!(actual, call_data);
    Ok(())
}

#[tokio::test]
async fn calls_with_empty_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/complex_types_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.get_empty_struct().call().await?;

        assert_eq!(response.value, EmptyStruct {});
    }
    {
        let response = contract_methods
            .input_empty_struct(EmptyStruct {})
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_struct_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let cocktail_in_bytes: Vec<u8> = vec![
        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3,
    ];

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(2),
        glass: 3,
    };

    // as slice
    let actual: Cocktail = cocktail_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Cocktail = (&cocktail_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Cocktail = cocktail_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn test_tuples() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/tuples"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.returns_tuple((1, 2)).call().await?;

        assert_eq!(response.value, (1, 2));
    }
    {
        // Tuple with struct.
        let my_struct_tuple = (
            42,
            Person {
                name: "Jane".try_into()?,
            },
        );
        let response = contract_methods
            .returns_struct_in_tuple(my_struct_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_struct_tuple);
    }
    {
        // Tuple with enum.
        let my_enum_tuple: (u64, State) = (42, State::A);

        let response = contract_methods
            .returns_enum_in_tuple(my_enum_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // Tuple with single element
        let my_enum_tuple = (123u64,);

        let response = contract_methods
            .single_element_tuple(my_enum_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // tuple with b256
        let id = *ContractId::zeroed();
        let my_b256_u8_tuple = (Bits256(id), 10);

        let response = contract_methods
            .tuple_with_b256(my_b256_u8_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_b256_u8_tuple);
    }

    Ok(())
}

#[tokio::test]
async fn test_evm_address() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/evm_address"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        // ANCHOR: evm_address_arg
        let b256 = Bits256::from_hex_str(
            "0x1616060606060606060606060606060606060606060606060606060606060606",
        )?;
        let evm_address = EvmAddress::from(b256);

        let call_handler = contract_instance
            .methods()
            .evm_address_as_input(evm_address);
        // ANCHOR_END: evm_address_arg

        assert!(call_handler.call().await?.value);
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_literal()
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_argument(b256)
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        contract_instance
            .methods()
            .get_array([42; 2])
            .call()
            .await?
            .value,
        [42; 2]
    );
    Ok(())
}

#[tokio::test]
async fn test_arrays_with_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let persons = [
        Person {
            name: "John".try_into()?,
        },
        Person {
            name: "Jane".try_into()?,
        },
    ];

    let contract_methods = contract_instance.methods();
    let response = contract_methods.array_of_structs(persons).call().await?;

    assert_eq!("John", response.value[0].name);
    assert_eq!("Jane", response.value[1].name);

    let states = [State::A, State::B];

    let response = contract_methods
        .array_of_enums(states.clone())
        .call()
        .await?;

    assert_eq!(states[0], response.value[0]);
    assert_eq!(states[1], response.value[1]);
    Ok(())
}

#[tokio::test]
async fn str_in_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/str_in_array"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let input = ["foo", "bar", "baz"].map(|str| str.try_into().unwrap());
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .take_array_string_shuffle(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["baz", "foo", "bar"]);

    let response = contract_methods
        .take_array_string_return_single(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["foo"]);

    let response = contract_methods
        .take_array_string_return_single_element(input)
        .call()
        .await?;

    assert_eq!(response.value, "bar");
    Ok(())
}

#[tokio::test]
async fn test_enum_inside_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_inside_struct"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(11),
        glass: 333,
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .return_enum_inside_struct(11)
        .call()
        .await?;

    assert_eq!(response.value, expected);

    let enum_inside_struct = Cocktail {
        the_thing_you_mix_in: Shaker::Cosmopolitan(444),
        glass: 555,
    };

    let response = contract_methods
        .take_enum_inside_struct(enum_inside_struct)
        .call()
        .await?;

    assert_eq!(response.value, 555);
    Ok(())
}

#[tokio::test]
async fn native_types_support() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/native_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let user = User {
        weight: 10,
        address: Address::zeroed(),
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods.wrapped_address(user).call().await?;

    assert_eq!(response.value.address, Address::zeroed());

    let response = contract_methods
        .unwrapped_address(Address::zeroed())
        .call()
        .await?;

    assert_eq!(
        response.value,
        Address::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")?
    );

    Ok(())
}

#[tokio::test]
async fn enum_coding_w_variable_width_variants() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of enum encoding width, then we'll
    // probably end up mangling arg_2 and onward which will fail this test.
    let expected = BigBundle {
        arg_1: EnumThatHasABigAndSmallVariant::Small(12345),
        arg_2: 6666,
        arg_3: 7777,
        arg_4: 8888,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_big_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_big_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_coding_w_unit_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of unit enum encoding width, then
    // we'll end up mangling arg_2
    let expected = UnitBundle {
        arg_1: UnitEnum::var2,
        arg_2: u64::MAX,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_unit_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_as_input"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = MaxedOutVariantsEnum::Variant255(11);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_max_variant().call().await?.value;
    assert_eq!(expected, actual);

    let expected = StandardEnum::Two(12345);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_standard_enum().call().await?.value;
    assert_eq!(expected, actual);

    let fuelvm_judgement = contract_methods
        .check_standard_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the standard enum correctly. Investigate!"
    );

    let expected = UnitEnum::Two;
    let actual = contract_methods.get_unit_enum().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the unit enum correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_enum_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let shaker_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];

    let expected = Shaker::Mojito(2);

    // as slice
    let actual: Shaker = shaker_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Shaker = (&shaker_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Shaker = shaker_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn type_inside_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/type_inside_enum"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // String inside enum
    let enum_string = SomeEnum::SomeStr("asdf".try_into()?);
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .str_inside_enum(enum_string.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_string);

    // Array inside enum
    let enum_array = SomeEnum::SomeArr([1, 2, 3, 4]);
    let response = contract_methods
        .arr_inside_enum(enum_array.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_array);

    // Struct inside enum
    let response = contract_methods
        .return_struct_inside_enum(11)
        .call()
        .await?;
    let expected = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 11 });
    assert_eq!(response.value, expected);

    let struct_inside_enum = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 66 });
    let response = contract_methods
        .take_struct_inside_enum(struct_inside_enum)
        .call()
        .await?;
    assert_eq!(response.value, 8888);

    // Enum inside enum
    let expected_enum = EnumLevel3::El2(EnumLevel2::El1(EnumLevel1::Num(42)));
    let response = contract_methods.get_nested_enum().call().await?;
    assert_eq!(response.value, expected_enum);

    let response = contract_methods
        .check_nested_enum_integrity(expected_enum)
        .call()
        .await?;
    assert!(
        response.value,
        "The FuelVM deems that we've not encoded the nested enum correctly. Investigate!"
    );

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_some_address = Some(expected_address);
    let response = contract_methods.get_some_address().call().await?;

    assert_eq!(response.value, expected_some_address);

    let expected_some_u64 = Some(10);
    let response = contract_methods.get_some_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_some_struct().call().await?;
    assert_eq!(response.value, Some(s.clone()));

    let response = contract_methods.get_some_enum().call().await?;
    assert_eq!(response.value, Some(e.clone()));

    let response = contract_methods.get_some_tuple().call().await?;
    assert_eq!(response.value, Some((s.clone(), e.clone())));

    let expected_none = None;
    let response = contract_methods.get_none().call().await?;

    assert_eq!(response.value, expected_none);

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_u64 = Some(36);
    let response = contract_methods
        .input_primitive(expected_u64)
        .call()
        .await?;

    assert!(response.value);

    let expected_struct = Some(s);
    let response = contract_methods
        .input_struct(expected_struct)
        .call()
        .await?;

    assert!(response.value);

    let expected_enum = Some(e);
    let response = contract_methods.input_enum(expected_enum).call().await?;

    assert!(response.value);

    let expected_none = None;
    let response = contract_methods.input_none(expected_none).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods.get_ok_address().call().await?;

    assert_eq!(response.value, expected_ok_address);

    let expected_some_u64 = Ok(10);
    let response = contract_methods.get_ok_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_ok_struct().call().await?;
    assert_eq!(response.value, Ok(s.clone()));

    let response = contract_methods.get_ok_enum().call().await?;
    assert_eq!(response.value, Ok(e.clone()));

    let response = contract_methods.get_ok_tuple().call().await?;
    assert_eq!(response.value, Ok((s, e)));

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.get_error().call().await?;

    assert_eq!(response.value, expected_error);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods
        .input_ok(expected_ok_address)
        .call()
        .await?;

    assert!(response.value);

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.input_error(expected_error).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_identity_address().call().await?;
    assert_eq!(response.value, Identity::Address(expected_address));

    let response = contract_methods.get_identity_contract_id().call().await?;
    assert_eq!(response.value, Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_struct_with_identity().call().await?;
    assert_eq!(response.value, s.clone());

    let response = contract_methods.get_enum_with_identity().call().await?;
    assert_eq!(response.value, e.clone());

    let response = contract_methods.get_identity_tuple().call().await?;
    assert_eq!(response.value, (s, e));

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods
        .input_identity(Identity::Address(expected_address))
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods
        .input_struct_with_identity(s)
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods.input_enum_with_identity(e).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_with_two_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    {
        let response = contract_instance
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }
    {
        let response = contract_instance2
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn generics_test() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/generics"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: generic
        // simple struct with a single generic param
        let arg1 = SimpleGeneric {
            single_generic_param: 123u64,
        };

        let result = contract_methods
            .struct_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
        // ANCHOR_END: generic
    }
    {
        // struct that delegates the generic param internally
        let arg1 = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "abc".try_into()?,
            },
        };

        let result = contract_methods
            .struct_delegating_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in an array
        let arg1 = StructWArrayGeneric { a: [1u32, 2u32] };

        let result = contract_methods
            .struct_w_generic_in_array(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has a generic struct in an array
        let inner = [
            StructWTwoGenerics {
                a: Bits256([1u8; 32]),
                b: 1,
            },
            StructWTwoGenerics {
                a: Bits256([2u8; 32]),
                b: 2,
            },
            StructWTwoGenerics {
                a: Bits256([3u8; 32]),
                b: 3,
            },
        ];
        let arg1 = StructWArrWGenericStruct { a: inner };

        let result = contract_methods
            .array_with_generic_struct(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in a tuple
        let arg1 = StructWTupleGeneric { a: (1, 2) };

        let result = contract_methods
            .struct_w_generic_in_tuple(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // enum with generic in variant
        let arg1 = EnumWGeneric::B(10);
        let result = contract_methods
            .enum_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        contract_methods
            .unused_generic_args(StructUnusedGeneric::new(15), EnumUnusedGeneric::One(15))
            .call()
            .await?;

        let (the_struct, the_enum) = contract_methods
            .used_and_unused_generic_args(
                StructUsedAndUnusedGenericParams::new(10u8),
                EnumUsedAndUnusedGenericParams::Two(11u8),
            )
            .call()
            .await?
            .value;

        assert_eq!(the_struct.field, 12u8);
        if let EnumUsedAndUnusedGenericParams::Two(val) = the_enum {
            assert_eq!(val, 13)
        } else {
            panic!("Expected the variant EnumUsedAndUnusedGenericParams::Two");
        }
    }
    {
        // complex case
        let pass_through = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "ab".try_into()?,
            },
        };
        let w_arr_generic = StructWArrayGeneric {
            a: [pass_through.clone(), pass_through],
        };

        let arg1 = MegaExample {
            a: ([Bits256([0; 32]), Bits256([0; 32])], "ab".try_into()?),
            b: vec![(
                [EnumWGeneric::B(StructWTupleGeneric {
                    a: (w_arr_generic.clone(), w_arr_generic),
                })],
                10u32,
            )],
        };
        contract_methods.complex_test(arg1.clone()).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_vectors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/vectors"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let methods = contract_instance.methods();

    {
        // vec of u32s
        let arg = vec![0, 1, 2];
        methods.u32_vec(arg).call().await?;
    }
    {
        // vec of vecs of u32s
        let arg = vec![vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_vec(arg.clone()).call().await?;
    }
    {
        // vec of structs
        // ANCHOR: passing_in_vec
        let arg = vec![SomeStruct { a: 0 }, SomeStruct { a: 1 }];
        methods.struct_in_vec(arg.clone()).call().await?;
        // ANCHOR_END: passing_in_vec
    }
    {
        // vec in struct
        let arg = SomeStruct { a: vec![0, 1, 2] };
        methods.vec_in_struct(arg.clone()).call().await?;
    }
    {
        // array in vec
        let arg = vec![[0u64, 1u64], [0u64, 1u64]];
        methods.array_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in array
        let arg = [vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_array(arg.clone()).call().await?;
    }
    {
        // vec in enum
        let arg = SomeEnum::a(vec![0, 1, 2]);
        methods.vec_in_enum(arg.clone()).call().await?;
    }
    {
        // enum in vec
        let arg = vec![SomeEnum::a(0), SomeEnum::a(1)];
        methods.enum_in_vec(arg.clone()).call().await?;
    }
    {
        // tuple in vec
        let arg = vec![(0, 0), (1, 1)];
        methods.tuple_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in tuple
        let arg = (vec![0, 1, 2], vec![0, 1, 2]);
        methods.vec_in_tuple(arg.clone()).call().await?;
    }
    {
        // vec in a vec in a struct in a vec
        let arg = vec![
            SomeStruct {
                a: vec![vec![0, 1, 2], vec![3, 4, 5]],
            },
            SomeStruct {
                a: vec![vec![6, 7, 8], vec![9, 10, 11]],
            },
        ];
        methods
            .vec_in_a_vec_in_a_struct_in_a_vec(arg.clone())
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_b256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        Bits256([2; 32]),
        contract_instance
            .methods()
            .b256_as_output()
            .call()
            .await?
            .value
    );

    {
        // ANCHOR: 256_arg
        let b256 = Bits256([1; 32]);

        let call_handler = contract_instance.methods().b256_as_input(b256);
        // ANCHOR_END: 256_arg

        assert!(call_handler.call().await?.value);
    }

    Ok(())
}

#[tokio::test]
async fn test_b512() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b512"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: b512_example
    let hi_bits = Bits256::from_hex_str(
        "0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c",
    )?;
    let lo_bits = Bits256::from_hex_str(
        "0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
    )?;
    let b512 = B512::from((hi_bits, lo_bits));
    // ANCHOR_END: b512_example

    assert_eq!(b512, contract_methods.b512_as_output().call().await?.value);

    {
        let lo_bits2 = Bits256::from_hex_str(
            "0x54ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
        )?;
        let b512 = B512::from((hi_bits, lo_bits2));

        assert!(contract_methods.b512_as_input(b512).call().await?.value);
    }

    Ok(())
}

fn u128_from(parts: (u64, u64)) -> u128 {
    let bytes: [u8; 16] = [parts.0.to_be_bytes(), parts.1.to_be_bytes()]
        .concat()
        .try_into()
        .unwrap();
    u128::from_be_bytes(bytes)
}

#[tokio::test]
async fn test_u128() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u128"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u128_from((1, 2));

        let actual = contract_methods.u128_sum_and_ret(arg).call().await?.value;

        let expected = arg + u128_from((3, 4));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u128_in_enum_output().call().await?.value;

        let expected = SomeEnum::B(u128_from((4, 4)));
        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u128_from((3, 3)));

        contract_methods.u128_in_enum_input(input).call().await?;
    }

    Ok(())
}

fn u256_from(parts: (u64, u64, u64, u64)) -> U256 {
    let bytes: [u8; 32] = [
        parts.0.to_be_bytes(),
        parts.1.to_be_bytes(),
        parts.2.to_be_bytes(),
        parts.3.to_be_bytes(),
    ]
    .concat()
    .try_into()
    .unwrap();
    U256::from(bytes)
}

#[tokio::test]
async fn test_u256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u256_from((1, 2, 3, 4));
        let actual = contract_methods.u256_sum_and_ret(arg).call().await?.value;
        let expected = arg + u256_from((3, 4, 5, 6));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u256_in_enum_output().call().await?.value;
        let expected = SomeEnum::B(u256_from((1, 2, 3, 4)));

        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u256_from((2, 3, 4, 5)));

        contract_methods.u256_in_enum_input(input).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_base_type_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: returning_vec
    let response = contract_methods.u8_in_vec(10).call().await?;
    assert_eq!(response.value, (0..10).collect::<Vec<_>>());
    // ANCHOR_END: returning_vec

    let response = contract_methods.u16_in_vec(11).call().await?;
    assert_eq!(response.value, (0..11).collect::<Vec<_>>());

    let response = contract_methods.u32_in_vec(12).call().await?;
    assert_eq!(response.value, (0..12).collect::<Vec<_>>());

    let response = contract_methods.u64_in_vec(13).call().await?;
    assert_eq!(response.value, (0..13).collect::<Vec<_>>());

    let response = contract_methods.bool_in_vec().call().await?;
    assert_eq!(response.value, [true, false, true, false].to_vec());

    let response = contract_methods.b256_in_vec(13).call().await?;
    assert_eq!(response.value, vec![Bits256([2; 32]); 13]);

    Ok(())
}

#[tokio::test]
async fn test_composite_types_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let expected: Vec<[u64; 4]> = vec![[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
        let response = contract_methods.array_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    {
        let expected: Vec<Pasta> = vec![
            Pasta::Tortelini(Bimbam {
                bim: 1111,
                bam: 2222_u32,
            }),
            Pasta::Rigatoni(1987),
            Pasta::Spaghetti(true),
        ];

        let response = contract_methods.enum_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<Bimbam> = vec![
            Bimbam {
                bim: 1111,
                bam: 2222_u32,
            },
            Bimbam {
                bim: 3333,
                bam: 4444_u32,
            },
            Bimbam {
                bim: 5555,
                bam: 6666_u32,
            },
        ];
        let response = contract_methods.struct_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<(u64, u32)> = vec![(1111, 2222_u32), (3333, 4444_u32), (5555, 6666_u32)];
        let response = contract_methods.tuple_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<SizedAsciiString<4>> =
            vec!["hell".try_into()?, "ello".try_into()?, "lloh".try_into()?];
        let response = contract_methods.str_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    Ok(())
}

#[tokio::test]
async fn test_bytes_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesOutputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.return_bytes(10).call().await?;

    assert_eq!(response.value, (0..10).collect::<Vec<_>>());

    Ok(())
}

#[tokio::test]
async fn test_bytes_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesInputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesInputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: bytes_arg
        let bytes = Bytes(vec![40, 41, 42]);

        contract_methods.accept_bytes(bytes).call().await?;
        // ANCHOR_END: bytes_arg
    }
    {
        let bytes = Bytes(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![bytes.clone(), bytes.clone()],
            inner_enum: SomeEnum::Second(bytes),
        };

        contract_methods.accept_nested_bytes(wrapper).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_raw_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RawSliceContract",
            project = "e2e/sway/types/contracts/raw_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RawSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    {
        for length in 0u8..=10 {
            let response = contract_methods.return_raw_slice(length).call().await?;
            assert_eq!(response.value, (0u8..length).collect::<Vec<u8>>());
        }
    }
    {
        contract_methods
            .accept_raw_slice(RawSlice(vec![40, 41, 42]))
            .call()
            .await?;
    }
    {
        let raw_slice = RawSlice(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![raw_slice.clone(), raw_slice.clone()],
            inner_enum: SomeEnum::Second(raw_slice),
        };

        contract_methods
            .accept_nested_raw_slice(wrapper)
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_string_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StringSliceContract",
            project = "e2e/sway/types/contracts/string_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StringSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .handles_str("contract-input".try_into()?)
        .call()
        .await?;
    assert_eq!(response.value, "contract-return");

    Ok(())
}

#[tokio::test]
async fn contract_std_lib_string() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StdLibString",
            project = "e2e/sway/types/contracts/std_lib_string"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StdLibString",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.return_dynamic_string().call().await?.value;
        assert_eq!(resp, "Hello World");
    }
    {
        let _resp = contract_methods
            .accepts_dynamic_string(String::from("Hello World"))
            .call()
            .await?;
    }
    {
        // confirm encoding/decoding a string wasn't faulty and led to too high gas consumption
        let _resp = contract_methods
            .echoes_dynamic_string(String::from("Hello Fuel"))
            .with_tx_policies(TxPolicies::default().with_script_gas_limit(3600))
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_heap_type_in_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_type_in_enums"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.returns_bytes_result(true).call().await?;
        let expected = Ok(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_result(false).call().await?;
        let expected = Err(TestError::Something([255u8, 255u8, 255u8, 255u8, 255u8]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(true).call().await?;
        let expected = Ok(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(false).call().await?;
        let expected = Err(TestError::Else(7777));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(true).call().await?;
        let expected = Ok("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_str_result(true).call().await?;
        let expected = Ok("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(true).call().await?;
        let expected = Some(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_vec_option(true).call().await?;
        let expected = Some(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_string_option(true).call().await?;
        let expected = Some("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_str_option(true).call().await?;
        let expected = Some("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }

    Ok(())
}

#[tokio::test]
async fn nested_heap_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let arr = [2u8, 4, 8];
    let struct_generics = StructGenerics {
        one: Bytes(arr.to_vec()),
        two: String::from("fuel"),
        three: RawSlice(arr.to_vec()),
    };

    let enum_vec = [struct_generics.clone(), struct_generics].to_vec();
    let expected = EnumGeneric::One(enum_vec);

    let result = contract_instance
        .methods()
        .nested_heap_types()
        .call()
        .await?;

    assert_eq!(result.value, expected);

    Ok(())
}\n```

If you have a hexadecimal value as a string and wish to convert it to Bits256, you may do so with from_hex_str:

```rust\nuse fuel_types::AssetId;
use fuels_macros::{Parameterize, Tokenizable, TryFrom};

use crate::types::errors::Result;

// A simple wrapper around [u8; 32] representing the `b256` type. Exists
// mainly so that we may differentiate `Parameterize` and `Tokenizable`
// implementations from what otherwise is just an array of 32 u8's.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct Bits256(pub [u8; 32]);

impl Bits256 {
    /// Returns `Self` with zeroes inside.
    pub fn zeroed() -> Self {
        Self([0; 32])
    }

    /// Create a new `Bits256` from a string representation of a hex.
    /// Accepts both `0x` prefixed and non-prefixed hex strings.
    pub fn from_hex_str(hex: &str) -> Result<Self> {
        let hex = if let Some(stripped_hex) = hex.strip_prefix("0x") {
            stripped_hex
        } else {
            hex
        };

        let mut bytes = [0u8; 32];
        hex::decode_to_slice(hex, &mut bytes as &mut [u8])?;

        Ok(Bits256(bytes))
    }
}

impl From<AssetId> for Bits256 {
    fn from(value: AssetId) -> Self {
        Self(value.into())
    }
}

// A simple wrapper around [Bits256; 2] representing the `B512` type.
#[derive(Debug, PartialEq, Eq, Copy, Clone, Parameterize, Tokenizable, TryFrom)]
#[FuelsCorePath = "crate"]
#[FuelsTypesPath = "crate::types"]
// ANCHOR: b512
pub struct B512 {
    pub bytes: [Bits256; 2],
}
// ANCHOR_END: b512

impl From<(Bits256, Bits256)> for B512 {
    fn from(bits_tuple: (Bits256, Bits256)) -> Self {
        B512 {
            bytes: [bits_tuple.0, bits_tuple.1],
        }
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone, Parameterize, Tokenizable, TryFrom)]
#[FuelsCorePath = "crate"]
#[FuelsTypesPath = "crate::types"]
// ANCHOR: evm_address
pub struct EvmAddress {
    // An evm address is only 20 bytes, the first 12 bytes should be set to 0
    value: Bits256,
}
// ANCHOR_END: evm_address
impl EvmAddress {
    fn new(b256: Bits256) -> Self {
        Self {
            value: Bits256(Self::clear_12_bytes(b256.0)),
        }
    }

    pub fn value(&self) -> Bits256 {
        self.value
    }

    // sets the leftmost 12 bytes to zero
    fn clear_12_bytes(bytes: [u8; 32]) -> [u8; 32] {
        let mut bytes = bytes;
        bytes[..12].copy_from_slice(&[0u8; 12]);

        bytes
    }
}

impl From<Bits256> for EvmAddress {
    fn from(b256: Bits256) -> Self {
        EvmAddress::new(b256)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        traits::{Parameterize, Tokenizable},
        types::{param_types::ParamType, Token},
    };

    #[test]
    fn from_hex_str_b256() -> Result<()> {
        // ANCHOR: from_hex_str
        let hex_str = "0101010101010101010101010101010101010101010101010101010101010101";

        let bits256 = Bits256::from_hex_str(hex_str)?;

        assert_eq!(bits256.0, [1u8; 32]);

        // With the `0x0` prefix
        // ANCHOR: hex_str_to_bits256
        let hex_str = "0x0101010101010101010101010101010101010101010101010101010101010101";

        let bits256 = Bits256::from_hex_str(hex_str)?;
        // ANCHOR_END: hex_str_to_bits256

        assert_eq!(bits256.0, [1u8; 32]);
        // ANCHOR_END: from_hex_str

        Ok(())
    }

    #[test]
    fn test_param_type_evm_addr() {
        assert_eq!(
            EvmAddress::param_type(),
            ParamType::Struct {
                name: "EvmAddress".to_string(),
                fields: vec![("value".to_string(), ParamType::B256)],
                generics: vec![]
            }
        );
    }

    #[test]
    fn evm_address_clears_first_12_bytes() -> Result<()> {
        let data = [1u8; 32];
        let address = EvmAddress::new(Bits256(data));

        let expected_data = Bits256([
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 1,
        ]);

        assert_eq!(address.value(), expected_data);

        Ok(())
    }

    #[test]
    fn test_into_token_evm_addr() {
        let bits = [1u8; 32];
        let evm_address = EvmAddress::from(Bits256(bits));

        let token = evm_address.into_token();

        let expected_data = [
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 1,
        ];

        assert_eq!(token, Token::Struct(vec![Token::B256(expected_data)]));
    }
}\n```

Bytes

In Fuel, a type called Bytes represents a collection of tightly-packed bytes. The Rust SDK represents Bytes as Bytes(Vec<u8>). Here's an example of using Bytes in a contract call:

```rust\nuse std::str::FromStr;

use fuels::{
    prelude::*,
    types::{Bits256, EvmAddress, Identity, SizedAsciiString, B512, U256},
};

pub fn null_contract_id() -> Bech32ContractId {
    // a bech32 contract address that decodes to [0u8;32]
    Bech32ContractId::from_str("fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2")
        .expect("is valid")
}

#[tokio::test]
async fn test_methods_typeless_argument() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/empty_arguments"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .method_with_empty_argument()
        .call()
        .await?;

    assert_eq!(response.value, 63);

    Ok(())
}

#[tokio::test]
async fn call_with_empty_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/types/contracts/call_empty_return"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let _response = contract_instance.methods().store_value(42).call().await?;
    Ok(())
}

#[tokio::test]
async fn call_with_structs() -> Result<()> {
    // Generates the bindings from the an ABI definition inline.
    // The generated bindings can be accessed through `MyContract`.
    // ANCHOR: struct_generation
    abigen!(Contract(name="MyContract",
                     abi="e2e/sway/types/contracts/complex_types_contract/out/release/complex_types_contract-abi.json"));

    // Here we can use `CounterConfig`, a struct originally
    // defined in the contract.
    let counter_config = CounterConfig {
        dummy: true,
        initial_value: 42,
    };
    // ANCHOR_END: struct_generation

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/types/contracts/complex_types_contract/out/release/complex_types_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id, wallet).methods();

    let response = contract_methods
        .initialize_counter(counter_config)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let response = contract_methods.increment_counter(10).call().await?;

    assert_eq!(52, response.value);

    Ok(())
}

#[tokio::test]
async fn abigen_different_structs_same_arg_name() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/two_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let param_one = StructOne { foo: 42 };
    let param_two = StructTwo { bar: 42 };

    let contract_methods = contract_instance.methods();
    let res_one = contract_methods.something(param_one).call().await?;

    assert_eq!(res_one.value, 43);

    let res_two = contract_methods.something_else(param_two).call().await?;

    assert_eq!(res_two.value, 41);
    Ok(())
}

#[tokio::test]
async fn nested_structs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/nested_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = AllStruct {
        some_struct: SomeStruct {
            field: 12345,
            field_2: true,
        },
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_struct().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_struct_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the argument correctly. Investigate!"
    );

    let memory_address = MemoryAddress {
        contract_id: ContractId::zeroed(),
        function_selector: 10,
        function_data: 0,
    };

    let call_data = CallData {
        memory_address,
        num_coins_to_forward: 10,
        asset_id_of_coins_to_forward: ContractId::zeroed(),
        amount_of_gas_to_forward: 5,
    };

    let actual = contract_methods
        .nested_struct_with_reserved_keyword_substring(call_data.clone())
        .call()
        .await?
        .value;

    assert_eq!(actual, call_data);
    Ok(())
}

#[tokio::test]
async fn calls_with_empty_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/complex_types_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.get_empty_struct().call().await?;

        assert_eq!(response.value, EmptyStruct {});
    }
    {
        let response = contract_methods
            .input_empty_struct(EmptyStruct {})
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_struct_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let cocktail_in_bytes: Vec<u8> = vec![
        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3,
    ];

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(2),
        glass: 3,
    };

    // as slice
    let actual: Cocktail = cocktail_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Cocktail = (&cocktail_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Cocktail = cocktail_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn test_tuples() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/tuples"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.returns_tuple((1, 2)).call().await?;

        assert_eq!(response.value, (1, 2));
    }
    {
        // Tuple with struct.
        let my_struct_tuple = (
            42,
            Person {
                name: "Jane".try_into()?,
            },
        );
        let response = contract_methods
            .returns_struct_in_tuple(my_struct_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_struct_tuple);
    }
    {
        // Tuple with enum.
        let my_enum_tuple: (u64, State) = (42, State::A);

        let response = contract_methods
            .returns_enum_in_tuple(my_enum_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // Tuple with single element
        let my_enum_tuple = (123u64,);

        let response = contract_methods
            .single_element_tuple(my_enum_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // tuple with b256
        let id = *ContractId::zeroed();
        let my_b256_u8_tuple = (Bits256(id), 10);

        let response = contract_methods
            .tuple_with_b256(my_b256_u8_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_b256_u8_tuple);
    }

    Ok(())
}

#[tokio::test]
async fn test_evm_address() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/evm_address"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        // ANCHOR: evm_address_arg
        let b256 = Bits256::from_hex_str(
            "0x1616060606060606060606060606060606060606060606060606060606060606",
        )?;
        let evm_address = EvmAddress::from(b256);

        let call_handler = contract_instance
            .methods()
            .evm_address_as_input(evm_address);
        // ANCHOR_END: evm_address_arg

        assert!(call_handler.call().await?.value);
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_literal()
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_argument(b256)
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        contract_instance
            .methods()
            .get_array([42; 2])
            .call()
            .await?
            .value,
        [42; 2]
    );
    Ok(())
}

#[tokio::test]
async fn test_arrays_with_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let persons = [
        Person {
            name: "John".try_into()?,
        },
        Person {
            name: "Jane".try_into()?,
        },
    ];

    let contract_methods = contract_instance.methods();
    let response = contract_methods.array_of_structs(persons).call().await?;

    assert_eq!("John", response.value[0].name);
    assert_eq!("Jane", response.value[1].name);

    let states = [State::A, State::B];

    let response = contract_methods
        .array_of_enums(states.clone())
        .call()
        .await?;

    assert_eq!(states[0], response.value[0]);
    assert_eq!(states[1], response.value[1]);
    Ok(())
}

#[tokio::test]
async fn str_in_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/str_in_array"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let input = ["foo", "bar", "baz"].map(|str| str.try_into().unwrap());
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .take_array_string_shuffle(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["baz", "foo", "bar"]);

    let response = contract_methods
        .take_array_string_return_single(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["foo"]);

    let response = contract_methods
        .take_array_string_return_single_element(input)
        .call()
        .await?;

    assert_eq!(response.value, "bar");
    Ok(())
}

#[tokio::test]
async fn test_enum_inside_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_inside_struct"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(11),
        glass: 333,
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .return_enum_inside_struct(11)
        .call()
        .await?;

    assert_eq!(response.value, expected);

    let enum_inside_struct = Cocktail {
        the_thing_you_mix_in: Shaker::Cosmopolitan(444),
        glass: 555,
    };

    let response = contract_methods
        .take_enum_inside_struct(enum_inside_struct)
        .call()
        .await?;

    assert_eq!(response.value, 555);
    Ok(())
}

#[tokio::test]
async fn native_types_support() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/native_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let user = User {
        weight: 10,
        address: Address::zeroed(),
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods.wrapped_address(user).call().await?;

    assert_eq!(response.value.address, Address::zeroed());

    let response = contract_methods
        .unwrapped_address(Address::zeroed())
        .call()
        .await?;

    assert_eq!(
        response.value,
        Address::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")?
    );

    Ok(())
}

#[tokio::test]
async fn enum_coding_w_variable_width_variants() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of enum encoding width, then we'll
    // probably end up mangling arg_2 and onward which will fail this test.
    let expected = BigBundle {
        arg_1: EnumThatHasABigAndSmallVariant::Small(12345),
        arg_2: 6666,
        arg_3: 7777,
        arg_4: 8888,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_big_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_big_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_coding_w_unit_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of unit enum encoding width, then
    // we'll end up mangling arg_2
    let expected = UnitBundle {
        arg_1: UnitEnum::var2,
        arg_2: u64::MAX,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_unit_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_as_input"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = MaxedOutVariantsEnum::Variant255(11);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_max_variant().call().await?.value;
    assert_eq!(expected, actual);

    let expected = StandardEnum::Two(12345);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_standard_enum().call().await?.value;
    assert_eq!(expected, actual);

    let fuelvm_judgement = contract_methods
        .check_standard_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the standard enum correctly. Investigate!"
    );

    let expected = UnitEnum::Two;
    let actual = contract_methods.get_unit_enum().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the unit enum correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_enum_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let shaker_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];

    let expected = Shaker::Mojito(2);

    // as slice
    let actual: Shaker = shaker_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Shaker = (&shaker_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Shaker = shaker_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn type_inside_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/type_inside_enum"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // String inside enum
    let enum_string = SomeEnum::SomeStr("asdf".try_into()?);
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .str_inside_enum(enum_string.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_string);

    // Array inside enum
    let enum_array = SomeEnum::SomeArr([1, 2, 3, 4]);
    let response = contract_methods
        .arr_inside_enum(enum_array.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_array);

    // Struct inside enum
    let response = contract_methods
        .return_struct_inside_enum(11)
        .call()
        .await?;
    let expected = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 11 });
    assert_eq!(response.value, expected);

    let struct_inside_enum = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 66 });
    let response = contract_methods
        .take_struct_inside_enum(struct_inside_enum)
        .call()
        .await?;
    assert_eq!(response.value, 8888);

    // Enum inside enum
    let expected_enum = EnumLevel3::El2(EnumLevel2::El1(EnumLevel1::Num(42)));
    let response = contract_methods.get_nested_enum().call().await?;
    assert_eq!(response.value, expected_enum);

    let response = contract_methods
        .check_nested_enum_integrity(expected_enum)
        .call()
        .await?;
    assert!(
        response.value,
        "The FuelVM deems that we've not encoded the nested enum correctly. Investigate!"
    );

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_some_address = Some(expected_address);
    let response = contract_methods.get_some_address().call().await?;

    assert_eq!(response.value, expected_some_address);

    let expected_some_u64 = Some(10);
    let response = contract_methods.get_some_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_some_struct().call().await?;
    assert_eq!(response.value, Some(s.clone()));

    let response = contract_methods.get_some_enum().call().await?;
    assert_eq!(response.value, Some(e.clone()));

    let response = contract_methods.get_some_tuple().call().await?;
    assert_eq!(response.value, Some((s.clone(), e.clone())));

    let expected_none = None;
    let response = contract_methods.get_none().call().await?;

    assert_eq!(response.value, expected_none);

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_u64 = Some(36);
    let response = contract_methods
        .input_primitive(expected_u64)
        .call()
        .await?;

    assert!(response.value);

    let expected_struct = Some(s);
    let response = contract_methods
        .input_struct(expected_struct)
        .call()
        .await?;

    assert!(response.value);

    let expected_enum = Some(e);
    let response = contract_methods.input_enum(expected_enum).call().await?;

    assert!(response.value);

    let expected_none = None;
    let response = contract_methods.input_none(expected_none).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods.get_ok_address().call().await?;

    assert_eq!(response.value, expected_ok_address);

    let expected_some_u64 = Ok(10);
    let response = contract_methods.get_ok_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_ok_struct().call().await?;
    assert_eq!(response.value, Ok(s.clone()));

    let response = contract_methods.get_ok_enum().call().await?;
    assert_eq!(response.value, Ok(e.clone()));

    let response = contract_methods.get_ok_tuple().call().await?;
    assert_eq!(response.value, Ok((s, e)));

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.get_error().call().await?;

    assert_eq!(response.value, expected_error);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods
        .input_ok(expected_ok_address)
        .call()
        .await?;

    assert!(response.value);

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.input_error(expected_error).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_identity_address().call().await?;
    assert_eq!(response.value, Identity::Address(expected_address));

    let response = contract_methods.get_identity_contract_id().call().await?;
    assert_eq!(response.value, Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_struct_with_identity().call().await?;
    assert_eq!(response.value, s.clone());

    let response = contract_methods.get_enum_with_identity().call().await?;
    assert_eq!(response.value, e.clone());

    let response = contract_methods.get_identity_tuple().call().await?;
    assert_eq!(response.value, (s, e));

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods
        .input_identity(Identity::Address(expected_address))
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods
        .input_struct_with_identity(s)
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods.input_enum_with_identity(e).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_with_two_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    {
        let response = contract_instance
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }
    {
        let response = contract_instance2
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn generics_test() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/generics"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: generic
        // simple struct with a single generic param
        let arg1 = SimpleGeneric {
            single_generic_param: 123u64,
        };

        let result = contract_methods
            .struct_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
        // ANCHOR_END: generic
    }
    {
        // struct that delegates the generic param internally
        let arg1 = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "abc".try_into()?,
            },
        };

        let result = contract_methods
            .struct_delegating_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in an array
        let arg1 = StructWArrayGeneric { a: [1u32, 2u32] };

        let result = contract_methods
            .struct_w_generic_in_array(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has a generic struct in an array
        let inner = [
            StructWTwoGenerics {
                a: Bits256([1u8; 32]),
                b: 1,
            },
            StructWTwoGenerics {
                a: Bits256([2u8; 32]),
                b: 2,
            },
            StructWTwoGenerics {
                a: Bits256([3u8; 32]),
                b: 3,
            },
        ];
        let arg1 = StructWArrWGenericStruct { a: inner };

        let result = contract_methods
            .array_with_generic_struct(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in a tuple
        let arg1 = StructWTupleGeneric { a: (1, 2) };

        let result = contract_methods
            .struct_w_generic_in_tuple(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // enum with generic in variant
        let arg1 = EnumWGeneric::B(10);
        let result = contract_methods
            .enum_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        contract_methods
            .unused_generic_args(StructUnusedGeneric::new(15), EnumUnusedGeneric::One(15))
            .call()
            .await?;

        let (the_struct, the_enum) = contract_methods
            .used_and_unused_generic_args(
                StructUsedAndUnusedGenericParams::new(10u8),
                EnumUsedAndUnusedGenericParams::Two(11u8),
            )
            .call()
            .await?
            .value;

        assert_eq!(the_struct.field, 12u8);
        if let EnumUsedAndUnusedGenericParams::Two(val) = the_enum {
            assert_eq!(val, 13)
        } else {
            panic!("Expected the variant EnumUsedAndUnusedGenericParams::Two");
        }
    }
    {
        // complex case
        let pass_through = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "ab".try_into()?,
            },
        };
        let w_arr_generic = StructWArrayGeneric {
            a: [pass_through.clone(), pass_through],
        };

        let arg1 = MegaExample {
            a: ([Bits256([0; 32]), Bits256([0; 32])], "ab".try_into()?),
            b: vec![(
                [EnumWGeneric::B(StructWTupleGeneric {
                    a: (w_arr_generic.clone(), w_arr_generic),
                })],
                10u32,
            )],
        };
        contract_methods.complex_test(arg1.clone()).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_vectors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/vectors"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let methods = contract_instance.methods();

    {
        // vec of u32s
        let arg = vec![0, 1, 2];
        methods.u32_vec(arg).call().await?;
    }
    {
        // vec of vecs of u32s
        let arg = vec![vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_vec(arg.clone()).call().await?;
    }
    {
        // vec of structs
        // ANCHOR: passing_in_vec
        let arg = vec![SomeStruct { a: 0 }, SomeStruct { a: 1 }];
        methods.struct_in_vec(arg.clone()).call().await?;
        // ANCHOR_END: passing_in_vec
    }
    {
        // vec in struct
        let arg = SomeStruct { a: vec![0, 1, 2] };
        methods.vec_in_struct(arg.clone()).call().await?;
    }
    {
        // array in vec
        let arg = vec![[0u64, 1u64], [0u64, 1u64]];
        methods.array_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in array
        let arg = [vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_array(arg.clone()).call().await?;
    }
    {
        // vec in enum
        let arg = SomeEnum::a(vec![0, 1, 2]);
        methods.vec_in_enum(arg.clone()).call().await?;
    }
    {
        // enum in vec
        let arg = vec![SomeEnum::a(0), SomeEnum::a(1)];
        methods.enum_in_vec(arg.clone()).call().await?;
    }
    {
        // tuple in vec
        let arg = vec![(0, 0), (1, 1)];
        methods.tuple_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in tuple
        let arg = (vec![0, 1, 2], vec![0, 1, 2]);
        methods.vec_in_tuple(arg.clone()).call().await?;
    }
    {
        // vec in a vec in a struct in a vec
        let arg = vec![
            SomeStruct {
                a: vec![vec![0, 1, 2], vec![3, 4, 5]],
            },
            SomeStruct {
                a: vec![vec![6, 7, 8], vec![9, 10, 11]],
            },
        ];
        methods
            .vec_in_a_vec_in_a_struct_in_a_vec(arg.clone())
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_b256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        Bits256([2; 32]),
        contract_instance
            .methods()
            .b256_as_output()
            .call()
            .await?
            .value
    );

    {
        // ANCHOR: 256_arg
        let b256 = Bits256([1; 32]);

        let call_handler = contract_instance.methods().b256_as_input(b256);
        // ANCHOR_END: 256_arg

        assert!(call_handler.call().await?.value);
    }

    Ok(())
}

#[tokio::test]
async fn test_b512() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b512"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: b512_example
    let hi_bits = Bits256::from_hex_str(
        "0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c",
    )?;
    let lo_bits = Bits256::from_hex_str(
        "0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
    )?;
    let b512 = B512::from((hi_bits, lo_bits));
    // ANCHOR_END: b512_example

    assert_eq!(b512, contract_methods.b512_as_output().call().await?.value);

    {
        let lo_bits2 = Bits256::from_hex_str(
            "0x54ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
        )?;
        let b512 = B512::from((hi_bits, lo_bits2));

        assert!(contract_methods.b512_as_input(b512).call().await?.value);
    }

    Ok(())
}

fn u128_from(parts: (u64, u64)) -> u128 {
    let bytes: [u8; 16] = [parts.0.to_be_bytes(), parts.1.to_be_bytes()]
        .concat()
        .try_into()
        .unwrap();
    u128::from_be_bytes(bytes)
}

#[tokio::test]
async fn test_u128() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u128"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u128_from((1, 2));

        let actual = contract_methods.u128_sum_and_ret(arg).call().await?.value;

        let expected = arg + u128_from((3, 4));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u128_in_enum_output().call().await?.value;

        let expected = SomeEnum::B(u128_from((4, 4)));
        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u128_from((3, 3)));

        contract_methods.u128_in_enum_input(input).call().await?;
    }

    Ok(())
}

fn u256_from(parts: (u64, u64, u64, u64)) -> U256 {
    let bytes: [u8; 32] = [
        parts.0.to_be_bytes(),
        parts.1.to_be_bytes(),
        parts.2.to_be_bytes(),
        parts.3.to_be_bytes(),
    ]
    .concat()
    .try_into()
    .unwrap();
    U256::from(bytes)
}

#[tokio::test]
async fn test_u256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u256_from((1, 2, 3, 4));
        let actual = contract_methods.u256_sum_and_ret(arg).call().await?.value;
        let expected = arg + u256_from((3, 4, 5, 6));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u256_in_enum_output().call().await?.value;
        let expected = SomeEnum::B(u256_from((1, 2, 3, 4)));

        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u256_from((2, 3, 4, 5)));

        contract_methods.u256_in_enum_input(input).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_base_type_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: returning_vec
    let response = contract_methods.u8_in_vec(10).call().await?;
    assert_eq!(response.value, (0..10).collect::<Vec<_>>());
    // ANCHOR_END: returning_vec

    let response = contract_methods.u16_in_vec(11).call().await?;
    assert_eq!(response.value, (0..11).collect::<Vec<_>>());

    let response = contract_methods.u32_in_vec(12).call().await?;
    assert_eq!(response.value, (0..12).collect::<Vec<_>>());

    let response = contract_methods.u64_in_vec(13).call().await?;
    assert_eq!(response.value, (0..13).collect::<Vec<_>>());

    let response = contract_methods.bool_in_vec().call().await?;
    assert_eq!(response.value, [true, false, true, false].to_vec());

    let response = contract_methods.b256_in_vec(13).call().await?;
    assert_eq!(response.value, vec![Bits256([2; 32]); 13]);

    Ok(())
}

#[tokio::test]
async fn test_composite_types_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let expected: Vec<[u64; 4]> = vec![[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
        let response = contract_methods.array_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    {
        let expected: Vec<Pasta> = vec![
            Pasta::Tortelini(Bimbam {
                bim: 1111,
                bam: 2222_u32,
            }),
            Pasta::Rigatoni(1987),
            Pasta::Spaghetti(true),
        ];

        let response = contract_methods.enum_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<Bimbam> = vec![
            Bimbam {
                bim: 1111,
                bam: 2222_u32,
            },
            Bimbam {
                bim: 3333,
                bam: 4444_u32,
            },
            Bimbam {
                bim: 5555,
                bam: 6666_u32,
            },
        ];
        let response = contract_methods.struct_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<(u64, u32)> = vec![(1111, 2222_u32), (3333, 4444_u32), (5555, 6666_u32)];
        let response = contract_methods.tuple_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<SizedAsciiString<4>> =
            vec!["hell".try_into()?, "ello".try_into()?, "lloh".try_into()?];
        let response = contract_methods.str_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    Ok(())
}

#[tokio::test]
async fn test_bytes_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesOutputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.return_bytes(10).call().await?;

    assert_eq!(response.value, (0..10).collect::<Vec<_>>());

    Ok(())
}

#[tokio::test]
async fn test_bytes_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesInputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesInputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: bytes_arg
        let bytes = Bytes(vec![40, 41, 42]);

        contract_methods.accept_bytes(bytes).call().await?;
        // ANCHOR_END: bytes_arg
    }
    {
        let bytes = Bytes(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![bytes.clone(), bytes.clone()],
            inner_enum: SomeEnum::Second(bytes),
        };

        contract_methods.accept_nested_bytes(wrapper).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_raw_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RawSliceContract",
            project = "e2e/sway/types/contracts/raw_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RawSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    {
        for length in 0u8..=10 {
            let response = contract_methods.return_raw_slice(length).call().await?;
            assert_eq!(response.value, (0u8..length).collect::<Vec<u8>>());
        }
    }
    {
        contract_methods
            .accept_raw_slice(RawSlice(vec![40, 41, 42]))
            .call()
            .await?;
    }
    {
        let raw_slice = RawSlice(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![raw_slice.clone(), raw_slice.clone()],
            inner_enum: SomeEnum::Second(raw_slice),
        };

        contract_methods
            .accept_nested_raw_slice(wrapper)
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_string_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StringSliceContract",
            project = "e2e/sway/types/contracts/string_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StringSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .handles_str("contract-input".try_into()?)
        .call()
        .await?;
    assert_eq!(response.value, "contract-return");

    Ok(())
}

#[tokio::test]
async fn contract_std_lib_string() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StdLibString",
            project = "e2e/sway/types/contracts/std_lib_string"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StdLibString",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.return_dynamic_string().call().await?.value;
        assert_eq!(resp, "Hello World");
    }
    {
        let _resp = contract_methods
            .accepts_dynamic_string(String::from("Hello World"))
            .call()
            .await?;
    }
    {
        // confirm encoding/decoding a string wasn't faulty and led to too high gas consumption
        let _resp = contract_methods
            .echoes_dynamic_string(String::from("Hello Fuel"))
            .with_tx_policies(TxPolicies::default().with_script_gas_limit(3600))
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_heap_type_in_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_type_in_enums"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.returns_bytes_result(true).call().await?;
        let expected = Ok(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_result(false).call().await?;
        let expected = Err(TestError::Something([255u8, 255u8, 255u8, 255u8, 255u8]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(true).call().await?;
        let expected = Ok(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(false).call().await?;
        let expected = Err(TestError::Else(7777));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(true).call().await?;
        let expected = Ok("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_str_result(true).call().await?;
        let expected = Ok("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(true).call().await?;
        let expected = Some(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_vec_option(true).call().await?;
        let expected = Some(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_string_option(true).call().await?;
        let expected = Some("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_str_option(true).call().await?;
        let expected = Some("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }

    Ok(())
}

#[tokio::test]
async fn nested_heap_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let arr = [2u8, 4, 8];
    let struct_generics = StructGenerics {
        one: Bytes(arr.to_vec()),
        two: String::from("fuel"),
        three: RawSlice(arr.to_vec()),
    };

    let enum_vec = [struct_generics.clone(), struct_generics].to_vec();
    let expected = EnumGeneric::One(enum_vec);

    let result = contract_instance
        .methods()
        .nested_heap_types()
        .call()
        .await?;

    assert_eq!(result.value, expected);

    Ok(())
}\n```

If you have a hexadecimal value as a string and wish to convert it to Bytes, you may do so with from_hex_str:

```rust\nuse crate::types::errors::Result;

#[derive(Debug, PartialEq, Clone, Eq)]
pub struct Bytes(pub Vec<u8>);

impl Bytes {
    /// Create a new `Bytes` from a string representation of a hex.
    /// Accepts both `0x` prefixed and non-prefixed hex strings.
    pub fn from_hex_str(hex: &str) -> Result<Self> {
        let hex = if let Some(stripped_hex) = hex.strip_prefix("0x") {
            stripped_hex
        } else {
            hex
        };
        let bytes = hex::decode(hex)?;

        Ok(Bytes(bytes))
    }
}

impl From<Bytes> for Vec<u8> {
    fn from(bytes: Bytes) -> Vec<u8> {
        bytes.0
    }
}

impl PartialEq<Vec<u8>> for Bytes {
    fn eq(&self, other: &Vec<u8>) -> bool {
        self.0 == *other
    }
}

impl PartialEq<Bytes> for Vec<u8> {
    fn eq(&self, other: &Bytes) -> bool {
        *self == other.0
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn from_hex_str_b256() -> Result<()> {
        // ANCHOR: bytes_from_hex_str
        let hex_str = "0101010101010101010101010101010101010101010101010101010101010101";

        let bytes = Bytes::from_hex_str(hex_str)?;

        assert_eq!(bytes.0, vec![1u8; 32]);

        // With the `0x0` prefix
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0101010101010101010101010101010101010101010101010101010101010101";

        let bytes = Bytes::from_hex_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32

        assert_eq!(bytes.0, vec![1u8; 32]);
        // ANCHOR_END: bytes_from_hex_str

        Ok(())
    }
}\n```

B512

In the Rust SDK, the B512 definition matches the Sway standard library type with the same name and will be converted accordingly when interacting with contracts:

```rust\nuse fuel_types::AssetId;
use fuels_macros::{Parameterize, Tokenizable, TryFrom};

use crate::types::errors::Result;

// A simple wrapper around [u8; 32] representing the `b256` type. Exists
// mainly so that we may differentiate `Parameterize` and `Tokenizable`
// implementations from what otherwise is just an array of 32 u8's.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct Bits256(pub [u8; 32]);

impl Bits256 {
    /// Returns `Self` with zeroes inside.
    pub fn zeroed() -> Self {
        Self([0; 32])
    }

    /// Create a new `Bits256` from a string representation of a hex.
    /// Accepts both `0x` prefixed and non-prefixed hex strings.
    pub fn from_hex_str(hex: &str) -> Result<Self> {
        let hex = if let Some(stripped_hex) = hex.strip_prefix("0x") {
            stripped_hex
        } else {
            hex
        };

        let mut bytes = [0u8; 32];
        hex::decode_to_slice(hex, &mut bytes as &mut [u8])?;

        Ok(Bits256(bytes))
    }
}

impl From<AssetId> for Bits256 {
    fn from(value: AssetId) -> Self {
        Self(value.into())
    }
}

// A simple wrapper around [Bits256; 2] representing the `B512` type.
#[derive(Debug, PartialEq, Eq, Copy, Clone, Parameterize, Tokenizable, TryFrom)]
#[FuelsCorePath = "crate"]
#[FuelsTypesPath = "crate::types"]
// ANCHOR: b512
pub struct B512 {
    pub bytes: [Bits256; 2],
}
// ANCHOR_END: b512

impl From<(Bits256, Bits256)> for B512 {
    fn from(bits_tuple: (Bits256, Bits256)) -> Self {
        B512 {
            bytes: [bits_tuple.0, bits_tuple.1],
        }
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone, Parameterize, Tokenizable, TryFrom)]
#[FuelsCorePath = "crate"]
#[FuelsTypesPath = "crate::types"]
// ANCHOR: evm_address
pub struct EvmAddress {
    // An evm address is only 20 bytes, the first 12 bytes should be set to 0
    value: Bits256,
}
// ANCHOR_END: evm_address
impl EvmAddress {
    fn new(b256: Bits256) -> Self {
        Self {
            value: Bits256(Self::clear_12_bytes(b256.0)),
        }
    }

    pub fn value(&self) -> Bits256 {
        self.value
    }

    // sets the leftmost 12 bytes to zero
    fn clear_12_bytes(bytes: [u8; 32]) -> [u8; 32] {
        let mut bytes = bytes;
        bytes[..12].copy_from_slice(&[0u8; 12]);

        bytes
    }
}

impl From<Bits256> for EvmAddress {
    fn from(b256: Bits256) -> Self {
        EvmAddress::new(b256)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        traits::{Parameterize, Tokenizable},
        types::{param_types::ParamType, Token},
    };

    #[test]
    fn from_hex_str_b256() -> Result<()> {
        // ANCHOR: from_hex_str
        let hex_str = "0101010101010101010101010101010101010101010101010101010101010101";

        let bits256 = Bits256::from_hex_str(hex_str)?;

        assert_eq!(bits256.0, [1u8; 32]);

        // With the `0x0` prefix
        // ANCHOR: hex_str_to_bits256
        let hex_str = "0x0101010101010101010101010101010101010101010101010101010101010101";

        let bits256 = Bits256::from_hex_str(hex_str)?;
        // ANCHOR_END: hex_str_to_bits256

        assert_eq!(bits256.0, [1u8; 32]);
        // ANCHOR_END: from_hex_str

        Ok(())
    }

    #[test]
    fn test_param_type_evm_addr() {
        assert_eq!(
            EvmAddress::param_type(),
            ParamType::Struct {
                name: "EvmAddress".to_string(),
                fields: vec![("value".to_string(), ParamType::B256)],
                generics: vec![]
            }
        );
    }

    #[test]
    fn evm_address_clears_first_12_bytes() -> Result<()> {
        let data = [1u8; 32];
        let address = EvmAddress::new(Bits256(data));

        let expected_data = Bits256([
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 1,
        ]);

        assert_eq!(address.value(), expected_data);

        Ok(())
    }

    #[test]
    fn test_into_token_evm_addr() {
        let bits = [1u8; 32];
        let evm_address = EvmAddress::from(Bits256(bits));

        let token = evm_address.into_token();

        let expected_data = [
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 1,
        ];

        assert_eq!(token, Token::Struct(vec![Token::B256(expected_data)]));
    }
}\n```

Here's an example:

```rust\nuse std::str::FromStr;

use fuels::{
    prelude::*,
    types::{Bits256, EvmAddress, Identity, SizedAsciiString, B512, U256},
};

pub fn null_contract_id() -> Bech32ContractId {
    // a bech32 contract address that decodes to [0u8;32]
    Bech32ContractId::from_str("fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2")
        .expect("is valid")
}

#[tokio::test]
async fn test_methods_typeless_argument() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/empty_arguments"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .method_with_empty_argument()
        .call()
        .await?;

    assert_eq!(response.value, 63);

    Ok(())
}

#[tokio::test]
async fn call_with_empty_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/types/contracts/call_empty_return"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let _response = contract_instance.methods().store_value(42).call().await?;
    Ok(())
}

#[tokio::test]
async fn call_with_structs() -> Result<()> {
    // Generates the bindings from the an ABI definition inline.
    // The generated bindings can be accessed through `MyContract`.
    // ANCHOR: struct_generation
    abigen!(Contract(name="MyContract",
                     abi="e2e/sway/types/contracts/complex_types_contract/out/release/complex_types_contract-abi.json"));

    // Here we can use `CounterConfig`, a struct originally
    // defined in the contract.
    let counter_config = CounterConfig {
        dummy: true,
        initial_value: 42,
    };
    // ANCHOR_END: struct_generation

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/types/contracts/complex_types_contract/out/release/complex_types_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id, wallet).methods();

    let response = contract_methods
        .initialize_counter(counter_config)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let response = contract_methods.increment_counter(10).call().await?;

    assert_eq!(52, response.value);

    Ok(())
}

#[tokio::test]
async fn abigen_different_structs_same_arg_name() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/two_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let param_one = StructOne { foo: 42 };
    let param_two = StructTwo { bar: 42 };

    let contract_methods = contract_instance.methods();
    let res_one = contract_methods.something(param_one).call().await?;

    assert_eq!(res_one.value, 43);

    let res_two = contract_methods.something_else(param_two).call().await?;

    assert_eq!(res_two.value, 41);
    Ok(())
}

#[tokio::test]
async fn nested_structs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/nested_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = AllStruct {
        some_struct: SomeStruct {
            field: 12345,
            field_2: true,
        },
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_struct().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_struct_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the argument correctly. Investigate!"
    );

    let memory_address = MemoryAddress {
        contract_id: ContractId::zeroed(),
        function_selector: 10,
        function_data: 0,
    };

    let call_data = CallData {
        memory_address,
        num_coins_to_forward: 10,
        asset_id_of_coins_to_forward: ContractId::zeroed(),
        amount_of_gas_to_forward: 5,
    };

    let actual = contract_methods
        .nested_struct_with_reserved_keyword_substring(call_data.clone())
        .call()
        .await?
        .value;

    assert_eq!(actual, call_data);
    Ok(())
}

#[tokio::test]
async fn calls_with_empty_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/complex_types_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.get_empty_struct().call().await?;

        assert_eq!(response.value, EmptyStruct {});
    }
    {
        let response = contract_methods
            .input_empty_struct(EmptyStruct {})
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_struct_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let cocktail_in_bytes: Vec<u8> = vec![
        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3,
    ];

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(2),
        glass: 3,
    };

    // as slice
    let actual: Cocktail = cocktail_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Cocktail = (&cocktail_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Cocktail = cocktail_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn test_tuples() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/tuples"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.returns_tuple((1, 2)).call().await?;

        assert_eq!(response.value, (1, 2));
    }
    {
        // Tuple with struct.
        let my_struct_tuple = (
            42,
            Person {
                name: "Jane".try_into()?,
            },
        );
        let response = contract_methods
            .returns_struct_in_tuple(my_struct_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_struct_tuple);
    }
    {
        // Tuple with enum.
        let my_enum_tuple: (u64, State) = (42, State::A);

        let response = contract_methods
            .returns_enum_in_tuple(my_enum_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // Tuple with single element
        let my_enum_tuple = (123u64,);

        let response = contract_methods
            .single_element_tuple(my_enum_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // tuple with b256
        let id = *ContractId::zeroed();
        let my_b256_u8_tuple = (Bits256(id), 10);

        let response = contract_methods
            .tuple_with_b256(my_b256_u8_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_b256_u8_tuple);
    }

    Ok(())
}

#[tokio::test]
async fn test_evm_address() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/evm_address"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        // ANCHOR: evm_address_arg
        let b256 = Bits256::from_hex_str(
            "0x1616060606060606060606060606060606060606060606060606060606060606",
        )?;
        let evm_address = EvmAddress::from(b256);

        let call_handler = contract_instance
            .methods()
            .evm_address_as_input(evm_address);
        // ANCHOR_END: evm_address_arg

        assert!(call_handler.call().await?.value);
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_literal()
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_argument(b256)
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        contract_instance
            .methods()
            .get_array([42; 2])
            .call()
            .await?
            .value,
        [42; 2]
    );
    Ok(())
}

#[tokio::test]
async fn test_arrays_with_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let persons = [
        Person {
            name: "John".try_into()?,
        },
        Person {
            name: "Jane".try_into()?,
        },
    ];

    let contract_methods = contract_instance.methods();
    let response = contract_methods.array_of_structs(persons).call().await?;

    assert_eq!("John", response.value[0].name);
    assert_eq!("Jane", response.value[1].name);

    let states = [State::A, State::B];

    let response = contract_methods
        .array_of_enums(states.clone())
        .call()
        .await?;

    assert_eq!(states[0], response.value[0]);
    assert_eq!(states[1], response.value[1]);
    Ok(())
}

#[tokio::test]
async fn str_in_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/str_in_array"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let input = ["foo", "bar", "baz"].map(|str| str.try_into().unwrap());
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .take_array_string_shuffle(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["baz", "foo", "bar"]);

    let response = contract_methods
        .take_array_string_return_single(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["foo"]);

    let response = contract_methods
        .take_array_string_return_single_element(input)
        .call()
        .await?;

    assert_eq!(response.value, "bar");
    Ok(())
}

#[tokio::test]
async fn test_enum_inside_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_inside_struct"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(11),
        glass: 333,
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .return_enum_inside_struct(11)
        .call()
        .await?;

    assert_eq!(response.value, expected);

    let enum_inside_struct = Cocktail {
        the_thing_you_mix_in: Shaker::Cosmopolitan(444),
        glass: 555,
    };

    let response = contract_methods
        .take_enum_inside_struct(enum_inside_struct)
        .call()
        .await?;

    assert_eq!(response.value, 555);
    Ok(())
}

#[tokio::test]
async fn native_types_support() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/native_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let user = User {
        weight: 10,
        address: Address::zeroed(),
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods.wrapped_address(user).call().await?;

    assert_eq!(response.value.address, Address::zeroed());

    let response = contract_methods
        .unwrapped_address(Address::zeroed())
        .call()
        .await?;

    assert_eq!(
        response.value,
        Address::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")?
    );

    Ok(())
}

#[tokio::test]
async fn enum_coding_w_variable_width_variants() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of enum encoding width, then we'll
    // probably end up mangling arg_2 and onward which will fail this test.
    let expected = BigBundle {
        arg_1: EnumThatHasABigAndSmallVariant::Small(12345),
        arg_2: 6666,
        arg_3: 7777,
        arg_4: 8888,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_big_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_big_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_coding_w_unit_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of unit enum encoding width, then
    // we'll end up mangling arg_2
    let expected = UnitBundle {
        arg_1: UnitEnum::var2,
        arg_2: u64::MAX,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_unit_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_as_input"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = MaxedOutVariantsEnum::Variant255(11);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_max_variant().call().await?.value;
    assert_eq!(expected, actual);

    let expected = StandardEnum::Two(12345);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_standard_enum().call().await?.value;
    assert_eq!(expected, actual);

    let fuelvm_judgement = contract_methods
        .check_standard_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the standard enum correctly. Investigate!"
    );

    let expected = UnitEnum::Two;
    let actual = contract_methods.get_unit_enum().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the unit enum correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_enum_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let shaker_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];

    let expected = Shaker::Mojito(2);

    // as slice
    let actual: Shaker = shaker_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Shaker = (&shaker_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Shaker = shaker_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn type_inside_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/type_inside_enum"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // String inside enum
    let enum_string = SomeEnum::SomeStr("asdf".try_into()?);
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .str_inside_enum(enum_string.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_string);

    // Array inside enum
    let enum_array = SomeEnum::SomeArr([1, 2, 3, 4]);
    let response = contract_methods
        .arr_inside_enum(enum_array.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_array);

    // Struct inside enum
    let response = contract_methods
        .return_struct_inside_enum(11)
        .call()
        .await?;
    let expected = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 11 });
    assert_eq!(response.value, expected);

    let struct_inside_enum = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 66 });
    let response = contract_methods
        .take_struct_inside_enum(struct_inside_enum)
        .call()
        .await?;
    assert_eq!(response.value, 8888);

    // Enum inside enum
    let expected_enum = EnumLevel3::El2(EnumLevel2::El1(EnumLevel1::Num(42)));
    let response = contract_methods.get_nested_enum().call().await?;
    assert_eq!(response.value, expected_enum);

    let response = contract_methods
        .check_nested_enum_integrity(expected_enum)
        .call()
        .await?;
    assert!(
        response.value,
        "The FuelVM deems that we've not encoded the nested enum correctly. Investigate!"
    );

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_some_address = Some(expected_address);
    let response = contract_methods.get_some_address().call().await?;

    assert_eq!(response.value, expected_some_address);

    let expected_some_u64 = Some(10);
    let response = contract_methods.get_some_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_some_struct().call().await?;
    assert_eq!(response.value, Some(s.clone()));

    let response = contract_methods.get_some_enum().call().await?;
    assert_eq!(response.value, Some(e.clone()));

    let response = contract_methods.get_some_tuple().call().await?;
    assert_eq!(response.value, Some((s.clone(), e.clone())));

    let expected_none = None;
    let response = contract_methods.get_none().call().await?;

    assert_eq!(response.value, expected_none);

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_u64 = Some(36);
    let response = contract_methods
        .input_primitive(expected_u64)
        .call()
        .await?;

    assert!(response.value);

    let expected_struct = Some(s);
    let response = contract_methods
        .input_struct(expected_struct)
        .call()
        .await?;

    assert!(response.value);

    let expected_enum = Some(e);
    let response = contract_methods.input_enum(expected_enum).call().await?;

    assert!(response.value);

    let expected_none = None;
    let response = contract_methods.input_none(expected_none).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods.get_ok_address().call().await?;

    assert_eq!(response.value, expected_ok_address);

    let expected_some_u64 = Ok(10);
    let response = contract_methods.get_ok_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_ok_struct().call().await?;
    assert_eq!(response.value, Ok(s.clone()));

    let response = contract_methods.get_ok_enum().call().await?;
    assert_eq!(response.value, Ok(e.clone()));

    let response = contract_methods.get_ok_tuple().call().await?;
    assert_eq!(response.value, Ok((s, e)));

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.get_error().call().await?;

    assert_eq!(response.value, expected_error);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods
        .input_ok(expected_ok_address)
        .call()
        .await?;

    assert!(response.value);

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.input_error(expected_error).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_identity_address().call().await?;
    assert_eq!(response.value, Identity::Address(expected_address));

    let response = contract_methods.get_identity_contract_id().call().await?;
    assert_eq!(response.value, Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_struct_with_identity().call().await?;
    assert_eq!(response.value, s.clone());

    let response = contract_methods.get_enum_with_identity().call().await?;
    assert_eq!(response.value, e.clone());

    let response = contract_methods.get_identity_tuple().call().await?;
    assert_eq!(response.value, (s, e));

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods
        .input_identity(Identity::Address(expected_address))
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods
        .input_struct_with_identity(s)
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods.input_enum_with_identity(e).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_with_two_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    {
        let response = contract_instance
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }
    {
        let response = contract_instance2
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn generics_test() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/generics"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: generic
        // simple struct with a single generic param
        let arg1 = SimpleGeneric {
            single_generic_param: 123u64,
        };

        let result = contract_methods
            .struct_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
        // ANCHOR_END: generic
    }
    {
        // struct that delegates the generic param internally
        let arg1 = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "abc".try_into()?,
            },
        };

        let result = contract_methods
            .struct_delegating_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in an array
        let arg1 = StructWArrayGeneric { a: [1u32, 2u32] };

        let result = contract_methods
            .struct_w_generic_in_array(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has a generic struct in an array
        let inner = [
            StructWTwoGenerics {
                a: Bits256([1u8; 32]),
                b: 1,
            },
            StructWTwoGenerics {
                a: Bits256([2u8; 32]),
                b: 2,
            },
            StructWTwoGenerics {
                a: Bits256([3u8; 32]),
                b: 3,
            },
        ];
        let arg1 = StructWArrWGenericStruct { a: inner };

        let result = contract_methods
            .array_with_generic_struct(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in a tuple
        let arg1 = StructWTupleGeneric { a: (1, 2) };

        let result = contract_methods
            .struct_w_generic_in_tuple(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // enum with generic in variant
        let arg1 = EnumWGeneric::B(10);
        let result = contract_methods
            .enum_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        contract_methods
            .unused_generic_args(StructUnusedGeneric::new(15), EnumUnusedGeneric::One(15))
            .call()
            .await?;

        let (the_struct, the_enum) = contract_methods
            .used_and_unused_generic_args(
                StructUsedAndUnusedGenericParams::new(10u8),
                EnumUsedAndUnusedGenericParams::Two(11u8),
            )
            .call()
            .await?
            .value;

        assert_eq!(the_struct.field, 12u8);
        if let EnumUsedAndUnusedGenericParams::Two(val) = the_enum {
            assert_eq!(val, 13)
        } else {
            panic!("Expected the variant EnumUsedAndUnusedGenericParams::Two");
        }
    }
    {
        // complex case
        let pass_through = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "ab".try_into()?,
            },
        };
        let w_arr_generic = StructWArrayGeneric {
            a: [pass_through.clone(), pass_through],
        };

        let arg1 = MegaExample {
            a: ([Bits256([0; 32]), Bits256([0; 32])], "ab".try_into()?),
            b: vec![(
                [EnumWGeneric::B(StructWTupleGeneric {
                    a: (w_arr_generic.clone(), w_arr_generic),
                })],
                10u32,
            )],
        };
        contract_methods.complex_test(arg1.clone()).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_vectors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/vectors"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let methods = contract_instance.methods();

    {
        // vec of u32s
        let arg = vec![0, 1, 2];
        methods.u32_vec(arg).call().await?;
    }
    {
        // vec of vecs of u32s
        let arg = vec![vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_vec(arg.clone()).call().await?;
    }
    {
        // vec of structs
        // ANCHOR: passing_in_vec
        let arg = vec![SomeStruct { a: 0 }, SomeStruct { a: 1 }];
        methods.struct_in_vec(arg.clone()).call().await?;
        // ANCHOR_END: passing_in_vec
    }
    {
        // vec in struct
        let arg = SomeStruct { a: vec![0, 1, 2] };
        methods.vec_in_struct(arg.clone()).call().await?;
    }
    {
        // array in vec
        let arg = vec![[0u64, 1u64], [0u64, 1u64]];
        methods.array_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in array
        let arg = [vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_array(arg.clone()).call().await?;
    }
    {
        // vec in enum
        let arg = SomeEnum::a(vec![0, 1, 2]);
        methods.vec_in_enum(arg.clone()).call().await?;
    }
    {
        // enum in vec
        let arg = vec![SomeEnum::a(0), SomeEnum::a(1)];
        methods.enum_in_vec(arg.clone()).call().await?;
    }
    {
        // tuple in vec
        let arg = vec![(0, 0), (1, 1)];
        methods.tuple_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in tuple
        let arg = (vec![0, 1, 2], vec![0, 1, 2]);
        methods.vec_in_tuple(arg.clone()).call().await?;
    }
    {
        // vec in a vec in a struct in a vec
        let arg = vec![
            SomeStruct {
                a: vec![vec![0, 1, 2], vec![3, 4, 5]],
            },
            SomeStruct {
                a: vec![vec![6, 7, 8], vec![9, 10, 11]],
            },
        ];
        methods
            .vec_in_a_vec_in_a_struct_in_a_vec(arg.clone())
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_b256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        Bits256([2; 32]),
        contract_instance
            .methods()
            .b256_as_output()
            .call()
            .await?
            .value
    );

    {
        // ANCHOR: 256_arg
        let b256 = Bits256([1; 32]);

        let call_handler = contract_instance.methods().b256_as_input(b256);
        // ANCHOR_END: 256_arg

        assert!(call_handler.call().await?.value);
    }

    Ok(())
}

#[tokio::test]
async fn test_b512() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b512"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: b512_example
    let hi_bits = Bits256::from_hex_str(
        "0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c",
    )?;
    let lo_bits = Bits256::from_hex_str(
        "0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
    )?;
    let b512 = B512::from((hi_bits, lo_bits));
    // ANCHOR_END: b512_example

    assert_eq!(b512, contract_methods.b512_as_output().call().await?.value);

    {
        let lo_bits2 = Bits256::from_hex_str(
            "0x54ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
        )?;
        let b512 = B512::from((hi_bits, lo_bits2));

        assert!(contract_methods.b512_as_input(b512).call().await?.value);
    }

    Ok(())
}

fn u128_from(parts: (u64, u64)) -> u128 {
    let bytes: [u8; 16] = [parts.0.to_be_bytes(), parts.1.to_be_bytes()]
        .concat()
        .try_into()
        .unwrap();
    u128::from_be_bytes(bytes)
}

#[tokio::test]
async fn test_u128() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u128"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u128_from((1, 2));

        let actual = contract_methods.u128_sum_and_ret(arg).call().await?.value;

        let expected = arg + u128_from((3, 4));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u128_in_enum_output().call().await?.value;

        let expected = SomeEnum::B(u128_from((4, 4)));
        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u128_from((3, 3)));

        contract_methods.u128_in_enum_input(input).call().await?;
    }

    Ok(())
}

fn u256_from(parts: (u64, u64, u64, u64)) -> U256 {
    let bytes: [u8; 32] = [
        parts.0.to_be_bytes(),
        parts.1.to_be_bytes(),
        parts.2.to_be_bytes(),
        parts.3.to_be_bytes(),
    ]
    .concat()
    .try_into()
    .unwrap();
    U256::from(bytes)
}

#[tokio::test]
async fn test_u256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u256_from((1, 2, 3, 4));
        let actual = contract_methods.u256_sum_and_ret(arg).call().await?.value;
        let expected = arg + u256_from((3, 4, 5, 6));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u256_in_enum_output().call().await?.value;
        let expected = SomeEnum::B(u256_from((1, 2, 3, 4)));

        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u256_from((2, 3, 4, 5)));

        contract_methods.u256_in_enum_input(input).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_base_type_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: returning_vec
    let response = contract_methods.u8_in_vec(10).call().await?;
    assert_eq!(response.value, (0..10).collect::<Vec<_>>());
    // ANCHOR_END: returning_vec

    let response = contract_methods.u16_in_vec(11).call().await?;
    assert_eq!(response.value, (0..11).collect::<Vec<_>>());

    let response = contract_methods.u32_in_vec(12).call().await?;
    assert_eq!(response.value, (0..12).collect::<Vec<_>>());

    let response = contract_methods.u64_in_vec(13).call().await?;
    assert_eq!(response.value, (0..13).collect::<Vec<_>>());

    let response = contract_methods.bool_in_vec().call().await?;
    assert_eq!(response.value, [true, false, true, false].to_vec());

    let response = contract_methods.b256_in_vec(13).call().await?;
    assert_eq!(response.value, vec![Bits256([2; 32]); 13]);

    Ok(())
}

#[tokio::test]
async fn test_composite_types_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let expected: Vec<[u64; 4]> = vec![[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
        let response = contract_methods.array_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    {
        let expected: Vec<Pasta> = vec![
            Pasta::Tortelini(Bimbam {
                bim: 1111,
                bam: 2222_u32,
            }),
            Pasta::Rigatoni(1987),
            Pasta::Spaghetti(true),
        ];

        let response = contract_methods.enum_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<Bimbam> = vec![
            Bimbam {
                bim: 1111,
                bam: 2222_u32,
            },
            Bimbam {
                bim: 3333,
                bam: 4444_u32,
            },
            Bimbam {
                bim: 5555,
                bam: 6666_u32,
            },
        ];
        let response = contract_methods.struct_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<(u64, u32)> = vec![(1111, 2222_u32), (3333, 4444_u32), (5555, 6666_u32)];
        let response = contract_methods.tuple_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<SizedAsciiString<4>> =
            vec!["hell".try_into()?, "ello".try_into()?, "lloh".try_into()?];
        let response = contract_methods.str_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    Ok(())
}

#[tokio::test]
async fn test_bytes_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesOutputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.return_bytes(10).call().await?;

    assert_eq!(response.value, (0..10).collect::<Vec<_>>());

    Ok(())
}

#[tokio::test]
async fn test_bytes_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesInputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesInputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: bytes_arg
        let bytes = Bytes(vec![40, 41, 42]);

        contract_methods.accept_bytes(bytes).call().await?;
        // ANCHOR_END: bytes_arg
    }
    {
        let bytes = Bytes(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![bytes.clone(), bytes.clone()],
            inner_enum: SomeEnum::Second(bytes),
        };

        contract_methods.accept_nested_bytes(wrapper).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_raw_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RawSliceContract",
            project = "e2e/sway/types/contracts/raw_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RawSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    {
        for length in 0u8..=10 {
            let response = contract_methods.return_raw_slice(length).call().await?;
            assert_eq!(response.value, (0u8..length).collect::<Vec<u8>>());
        }
    }
    {
        contract_methods
            .accept_raw_slice(RawSlice(vec![40, 41, 42]))
            .call()
            .await?;
    }
    {
        let raw_slice = RawSlice(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![raw_slice.clone(), raw_slice.clone()],
            inner_enum: SomeEnum::Second(raw_slice),
        };

        contract_methods
            .accept_nested_raw_slice(wrapper)
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_string_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StringSliceContract",
            project = "e2e/sway/types/contracts/string_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StringSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .handles_str("contract-input".try_into()?)
        .call()
        .await?;
    assert_eq!(response.value, "contract-return");

    Ok(())
}

#[tokio::test]
async fn contract_std_lib_string() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StdLibString",
            project = "e2e/sway/types/contracts/std_lib_string"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StdLibString",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.return_dynamic_string().call().await?.value;
        assert_eq!(resp, "Hello World");
    }
    {
        let _resp = contract_methods
            .accepts_dynamic_string(String::from("Hello World"))
            .call()
            .await?;
    }
    {
        // confirm encoding/decoding a string wasn't faulty and led to too high gas consumption
        let _resp = contract_methods
            .echoes_dynamic_string(String::from("Hello Fuel"))
            .with_tx_policies(TxPolicies::default().with_script_gas_limit(3600))
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_heap_type_in_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_type_in_enums"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.returns_bytes_result(true).call().await?;
        let expected = Ok(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_result(false).call().await?;
        let expected = Err(TestError::Something([255u8, 255u8, 255u8, 255u8, 255u8]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(true).call().await?;
        let expected = Ok(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(false).call().await?;
        let expected = Err(TestError::Else(7777));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(true).call().await?;
        let expected = Ok("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_str_result(true).call().await?;
        let expected = Ok("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(true).call().await?;
        let expected = Some(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_vec_option(true).call().await?;
        let expected = Some(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_string_option(true).call().await?;
        let expected = Some("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_str_option(true).call().await?;
        let expected = Some("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }

    Ok(())
}

#[tokio::test]
async fn nested_heap_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let arr = [2u8, 4, 8];
    let struct_generics = StructGenerics {
        one: Bytes(arr.to_vec()),
        two: String::from("fuel"),
        three: RawSlice(arr.to_vec()),
    };

    let enum_vec = [struct_generics.clone(), struct_generics].to_vec();
    let expected = EnumGeneric::One(enum_vec);

    let result = contract_instance
        .methods()
        .nested_heap_types()
        .call()
        .await?;

    assert_eq!(result.value, expected);

    Ok(())
}\n```

EvmAddress

In the Rust SDK, Ethereum Virtual Machine (EVM) addresses can be represented with the EvmAddress type. Its definition matches with the Sway standard library type with the same name and will be converted accordingly when interacting with contracts:

```rust\nuse fuel_types::AssetId;
use fuels_macros::{Parameterize, Tokenizable, TryFrom};

use crate::types::errors::Result;

// A simple wrapper around [u8; 32] representing the `b256` type. Exists
// mainly so that we may differentiate `Parameterize` and `Tokenizable`
// implementations from what otherwise is just an array of 32 u8's.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct Bits256(pub [u8; 32]);

impl Bits256 {
    /// Returns `Self` with zeroes inside.
    pub fn zeroed() -> Self {
        Self([0; 32])
    }

    /// Create a new `Bits256` from a string representation of a hex.
    /// Accepts both `0x` prefixed and non-prefixed hex strings.
    pub fn from_hex_str(hex: &str) -> Result<Self> {
        let hex = if let Some(stripped_hex) = hex.strip_prefix("0x") {
            stripped_hex
        } else {
            hex
        };

        let mut bytes = [0u8; 32];
        hex::decode_to_slice(hex, &mut bytes as &mut [u8])?;

        Ok(Bits256(bytes))
    }
}

impl From<AssetId> for Bits256 {
    fn from(value: AssetId) -> Self {
        Self(value.into())
    }
}

// A simple wrapper around [Bits256; 2] representing the `B512` type.
#[derive(Debug, PartialEq, Eq, Copy, Clone, Parameterize, Tokenizable, TryFrom)]
#[FuelsCorePath = "crate"]
#[FuelsTypesPath = "crate::types"]
// ANCHOR: b512
pub struct B512 {
    pub bytes: [Bits256; 2],
}
// ANCHOR_END: b512

impl From<(Bits256, Bits256)> for B512 {
    fn from(bits_tuple: (Bits256, Bits256)) -> Self {
        B512 {
            bytes: [bits_tuple.0, bits_tuple.1],
        }
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone, Parameterize, Tokenizable, TryFrom)]
#[FuelsCorePath = "crate"]
#[FuelsTypesPath = "crate::types"]
// ANCHOR: evm_address
pub struct EvmAddress {
    // An evm address is only 20 bytes, the first 12 bytes should be set to 0
    value: Bits256,
}
// ANCHOR_END: evm_address
impl EvmAddress {
    fn new(b256: Bits256) -> Self {
        Self {
            value: Bits256(Self::clear_12_bytes(b256.0)),
        }
    }

    pub fn value(&self) -> Bits256 {
        self.value
    }

    // sets the leftmost 12 bytes to zero
    fn clear_12_bytes(bytes: [u8; 32]) -> [u8; 32] {
        let mut bytes = bytes;
        bytes[..12].copy_from_slice(&[0u8; 12]);

        bytes
    }
}

impl From<Bits256> for EvmAddress {
    fn from(b256: Bits256) -> Self {
        EvmAddress::new(b256)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        traits::{Parameterize, Tokenizable},
        types::{param_types::ParamType, Token},
    };

    #[test]
    fn from_hex_str_b256() -> Result<()> {
        // ANCHOR: from_hex_str
        let hex_str = "0101010101010101010101010101010101010101010101010101010101010101";

        let bits256 = Bits256::from_hex_str(hex_str)?;

        assert_eq!(bits256.0, [1u8; 32]);

        // With the `0x0` prefix
        // ANCHOR: hex_str_to_bits256
        let hex_str = "0x0101010101010101010101010101010101010101010101010101010101010101";

        let bits256 = Bits256::from_hex_str(hex_str)?;
        // ANCHOR_END: hex_str_to_bits256

        assert_eq!(bits256.0, [1u8; 32]);
        // ANCHOR_END: from_hex_str

        Ok(())
    }

    #[test]
    fn test_param_type_evm_addr() {
        assert_eq!(
            EvmAddress::param_type(),
            ParamType::Struct {
                name: "EvmAddress".to_string(),
                fields: vec![("value".to_string(), ParamType::B256)],
                generics: vec![]
            }
        );
    }

    #[test]
    fn evm_address_clears_first_12_bytes() -> Result<()> {
        let data = [1u8; 32];
        let address = EvmAddress::new(Bits256(data));

        let expected_data = Bits256([
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 1,
        ]);

        assert_eq!(address.value(), expected_data);

        Ok(())
    }

    #[test]
    fn test_into_token_evm_addr() {
        let bits = [1u8; 32];
        let evm_address = EvmAddress::from(Bits256(bits));

        let token = evm_address.into_token();

        let expected_data = [
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 1,
        ];

        assert_eq!(token, Token::Struct(vec![Token::B256(expected_data)]));
    }
}\n```

Here's an example:

```rust\nuse std::str::FromStr;

use fuels::{
    prelude::*,
    types::{Bits256, EvmAddress, Identity, SizedAsciiString, B512, U256},
};

pub fn null_contract_id() -> Bech32ContractId {
    // a bech32 contract address that decodes to [0u8;32]
    Bech32ContractId::from_str("fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2")
        .expect("is valid")
}

#[tokio::test]
async fn test_methods_typeless_argument() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/empty_arguments"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .method_with_empty_argument()
        .call()
        .await?;

    assert_eq!(response.value, 63);

    Ok(())
}

#[tokio::test]
async fn call_with_empty_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/types/contracts/call_empty_return"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let _response = contract_instance.methods().store_value(42).call().await?;
    Ok(())
}

#[tokio::test]
async fn call_with_structs() -> Result<()> {
    // Generates the bindings from the an ABI definition inline.
    // The generated bindings can be accessed through `MyContract`.
    // ANCHOR: struct_generation
    abigen!(Contract(name="MyContract",
                     abi="e2e/sway/types/contracts/complex_types_contract/out/release/complex_types_contract-abi.json"));

    // Here we can use `CounterConfig`, a struct originally
    // defined in the contract.
    let counter_config = CounterConfig {
        dummy: true,
        initial_value: 42,
    };
    // ANCHOR_END: struct_generation

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/types/contracts/complex_types_contract/out/release/complex_types_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id, wallet).methods();

    let response = contract_methods
        .initialize_counter(counter_config)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let response = contract_methods.increment_counter(10).call().await?;

    assert_eq!(52, response.value);

    Ok(())
}

#[tokio::test]
async fn abigen_different_structs_same_arg_name() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/two_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let param_one = StructOne { foo: 42 };
    let param_two = StructTwo { bar: 42 };

    let contract_methods = contract_instance.methods();
    let res_one = contract_methods.something(param_one).call().await?;

    assert_eq!(res_one.value, 43);

    let res_two = contract_methods.something_else(param_two).call().await?;

    assert_eq!(res_two.value, 41);
    Ok(())
}

#[tokio::test]
async fn nested_structs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/nested_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = AllStruct {
        some_struct: SomeStruct {
            field: 12345,
            field_2: true,
        },
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_struct().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_struct_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the argument correctly. Investigate!"
    );

    let memory_address = MemoryAddress {
        contract_id: ContractId::zeroed(),
        function_selector: 10,
        function_data: 0,
    };

    let call_data = CallData {
        memory_address,
        num_coins_to_forward: 10,
        asset_id_of_coins_to_forward: ContractId::zeroed(),
        amount_of_gas_to_forward: 5,
    };

    let actual = contract_methods
        .nested_struct_with_reserved_keyword_substring(call_data.clone())
        .call()
        .await?
        .value;

    assert_eq!(actual, call_data);
    Ok(())
}

#[tokio::test]
async fn calls_with_empty_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/complex_types_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.get_empty_struct().call().await?;

        assert_eq!(response.value, EmptyStruct {});
    }
    {
        let response = contract_methods
            .input_empty_struct(EmptyStruct {})
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_struct_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let cocktail_in_bytes: Vec<u8> = vec![
        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3,
    ];

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(2),
        glass: 3,
    };

    // as slice
    let actual: Cocktail = cocktail_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Cocktail = (&cocktail_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Cocktail = cocktail_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn test_tuples() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/tuples"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.returns_tuple((1, 2)).call().await?;

        assert_eq!(response.value, (1, 2));
    }
    {
        // Tuple with struct.
        let my_struct_tuple = (
            42,
            Person {
                name: "Jane".try_into()?,
            },
        );
        let response = contract_methods
            .returns_struct_in_tuple(my_struct_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_struct_tuple);
    }
    {
        // Tuple with enum.
        let my_enum_tuple: (u64, State) = (42, State::A);

        let response = contract_methods
            .returns_enum_in_tuple(my_enum_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // Tuple with single element
        let my_enum_tuple = (123u64,);

        let response = contract_methods
            .single_element_tuple(my_enum_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // tuple with b256
        let id = *ContractId::zeroed();
        let my_b256_u8_tuple = (Bits256(id), 10);

        let response = contract_methods
            .tuple_with_b256(my_b256_u8_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_b256_u8_tuple);
    }

    Ok(())
}

#[tokio::test]
async fn test_evm_address() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/evm_address"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        // ANCHOR: evm_address_arg
        let b256 = Bits256::from_hex_str(
            "0x1616060606060606060606060606060606060606060606060606060606060606",
        )?;
        let evm_address = EvmAddress::from(b256);

        let call_handler = contract_instance
            .methods()
            .evm_address_as_input(evm_address);
        // ANCHOR_END: evm_address_arg

        assert!(call_handler.call().await?.value);
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_literal()
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_argument(b256)
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        contract_instance
            .methods()
            .get_array([42; 2])
            .call()
            .await?
            .value,
        [42; 2]
    );
    Ok(())
}

#[tokio::test]
async fn test_arrays_with_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let persons = [
        Person {
            name: "John".try_into()?,
        },
        Person {
            name: "Jane".try_into()?,
        },
    ];

    let contract_methods = contract_instance.methods();
    let response = contract_methods.array_of_structs(persons).call().await?;

    assert_eq!("John", response.value[0].name);
    assert_eq!("Jane", response.value[1].name);

    let states = [State::A, State::B];

    let response = contract_methods
        .array_of_enums(states.clone())
        .call()
        .await?;

    assert_eq!(states[0], response.value[0]);
    assert_eq!(states[1], response.value[1]);
    Ok(())
}

#[tokio::test]
async fn str_in_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/str_in_array"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let input = ["foo", "bar", "baz"].map(|str| str.try_into().unwrap());
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .take_array_string_shuffle(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["baz", "foo", "bar"]);

    let response = contract_methods
        .take_array_string_return_single(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["foo"]);

    let response = contract_methods
        .take_array_string_return_single_element(input)
        .call()
        .await?;

    assert_eq!(response.value, "bar");
    Ok(())
}

#[tokio::test]
async fn test_enum_inside_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_inside_struct"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(11),
        glass: 333,
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .return_enum_inside_struct(11)
        .call()
        .await?;

    assert_eq!(response.value, expected);

    let enum_inside_struct = Cocktail {
        the_thing_you_mix_in: Shaker::Cosmopolitan(444),
        glass: 555,
    };

    let response = contract_methods
        .take_enum_inside_struct(enum_inside_struct)
        .call()
        .await?;

    assert_eq!(response.value, 555);
    Ok(())
}

#[tokio::test]
async fn native_types_support() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/native_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let user = User {
        weight: 10,
        address: Address::zeroed(),
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods.wrapped_address(user).call().await?;

    assert_eq!(response.value.address, Address::zeroed());

    let response = contract_methods
        .unwrapped_address(Address::zeroed())
        .call()
        .await?;

    assert_eq!(
        response.value,
        Address::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")?
    );

    Ok(())
}

#[tokio::test]
async fn enum_coding_w_variable_width_variants() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of enum encoding width, then we'll
    // probably end up mangling arg_2 and onward which will fail this test.
    let expected = BigBundle {
        arg_1: EnumThatHasABigAndSmallVariant::Small(12345),
        arg_2: 6666,
        arg_3: 7777,
        arg_4: 8888,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_big_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_big_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_coding_w_unit_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of unit enum encoding width, then
    // we'll end up mangling arg_2
    let expected = UnitBundle {
        arg_1: UnitEnum::var2,
        arg_2: u64::MAX,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_unit_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_as_input"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = MaxedOutVariantsEnum::Variant255(11);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_max_variant().call().await?.value;
    assert_eq!(expected, actual);

    let expected = StandardEnum::Two(12345);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_standard_enum().call().await?.value;
    assert_eq!(expected, actual);

    let fuelvm_judgement = contract_methods
        .check_standard_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the standard enum correctly. Investigate!"
    );

    let expected = UnitEnum::Two;
    let actual = contract_methods.get_unit_enum().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the unit enum correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_enum_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let shaker_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];

    let expected = Shaker::Mojito(2);

    // as slice
    let actual: Shaker = shaker_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Shaker = (&shaker_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Shaker = shaker_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn type_inside_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/type_inside_enum"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // String inside enum
    let enum_string = SomeEnum::SomeStr("asdf".try_into()?);
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .str_inside_enum(enum_string.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_string);

    // Array inside enum
    let enum_array = SomeEnum::SomeArr([1, 2, 3, 4]);
    let response = contract_methods
        .arr_inside_enum(enum_array.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_array);

    // Struct inside enum
    let response = contract_methods
        .return_struct_inside_enum(11)
        .call()
        .await?;
    let expected = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 11 });
    assert_eq!(response.value, expected);

    let struct_inside_enum = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 66 });
    let response = contract_methods
        .take_struct_inside_enum(struct_inside_enum)
        .call()
        .await?;
    assert_eq!(response.value, 8888);

    // Enum inside enum
    let expected_enum = EnumLevel3::El2(EnumLevel2::El1(EnumLevel1::Num(42)));
    let response = contract_methods.get_nested_enum().call().await?;
    assert_eq!(response.value, expected_enum);

    let response = contract_methods
        .check_nested_enum_integrity(expected_enum)
        .call()
        .await?;
    assert!(
        response.value,
        "The FuelVM deems that we've not encoded the nested enum correctly. Investigate!"
    );

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_some_address = Some(expected_address);
    let response = contract_methods.get_some_address().call().await?;

    assert_eq!(response.value, expected_some_address);

    let expected_some_u64 = Some(10);
    let response = contract_methods.get_some_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_some_struct().call().await?;
    assert_eq!(response.value, Some(s.clone()));

    let response = contract_methods.get_some_enum().call().await?;
    assert_eq!(response.value, Some(e.clone()));

    let response = contract_methods.get_some_tuple().call().await?;
    assert_eq!(response.value, Some((s.clone(), e.clone())));

    let expected_none = None;
    let response = contract_methods.get_none().call().await?;

    assert_eq!(response.value, expected_none);

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_u64 = Some(36);
    let response = contract_methods
        .input_primitive(expected_u64)
        .call()
        .await?;

    assert!(response.value);

    let expected_struct = Some(s);
    let response = contract_methods
        .input_struct(expected_struct)
        .call()
        .await?;

    assert!(response.value);

    let expected_enum = Some(e);
    let response = contract_methods.input_enum(expected_enum).call().await?;

    assert!(response.value);

    let expected_none = None;
    let response = contract_methods.input_none(expected_none).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods.get_ok_address().call().await?;

    assert_eq!(response.value, expected_ok_address);

    let expected_some_u64 = Ok(10);
    let response = contract_methods.get_ok_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_ok_struct().call().await?;
    assert_eq!(response.value, Ok(s.clone()));

    let response = contract_methods.get_ok_enum().call().await?;
    assert_eq!(response.value, Ok(e.clone()));

    let response = contract_methods.get_ok_tuple().call().await?;
    assert_eq!(response.value, Ok((s, e)));

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.get_error().call().await?;

    assert_eq!(response.value, expected_error);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods
        .input_ok(expected_ok_address)
        .call()
        .await?;

    assert!(response.value);

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.input_error(expected_error).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_identity_address().call().await?;
    assert_eq!(response.value, Identity::Address(expected_address));

    let response = contract_methods.get_identity_contract_id().call().await?;
    assert_eq!(response.value, Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_struct_with_identity().call().await?;
    assert_eq!(response.value, s.clone());

    let response = contract_methods.get_enum_with_identity().call().await?;
    assert_eq!(response.value, e.clone());

    let response = contract_methods.get_identity_tuple().call().await?;
    assert_eq!(response.value, (s, e));

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods
        .input_identity(Identity::Address(expected_address))
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods
        .input_struct_with_identity(s)
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods.input_enum_with_identity(e).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_with_two_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    {
        let response = contract_instance
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }
    {
        let response = contract_instance2
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn generics_test() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/generics"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: generic
        // simple struct with a single generic param
        let arg1 = SimpleGeneric {
            single_generic_param: 123u64,
        };

        let result = contract_methods
            .struct_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
        // ANCHOR_END: generic
    }
    {
        // struct that delegates the generic param internally
        let arg1 = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "abc".try_into()?,
            },
        };

        let result = contract_methods
            .struct_delegating_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in an array
        let arg1 = StructWArrayGeneric { a: [1u32, 2u32] };

        let result = contract_methods
            .struct_w_generic_in_array(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has a generic struct in an array
        let inner = [
            StructWTwoGenerics {
                a: Bits256([1u8; 32]),
                b: 1,
            },
            StructWTwoGenerics {
                a: Bits256([2u8; 32]),
                b: 2,
            },
            StructWTwoGenerics {
                a: Bits256([3u8; 32]),
                b: 3,
            },
        ];
        let arg1 = StructWArrWGenericStruct { a: inner };

        let result = contract_methods
            .array_with_generic_struct(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in a tuple
        let arg1 = StructWTupleGeneric { a: (1, 2) };

        let result = contract_methods
            .struct_w_generic_in_tuple(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // enum with generic in variant
        let arg1 = EnumWGeneric::B(10);
        let result = contract_methods
            .enum_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        contract_methods
            .unused_generic_args(StructUnusedGeneric::new(15), EnumUnusedGeneric::One(15))
            .call()
            .await?;

        let (the_struct, the_enum) = contract_methods
            .used_and_unused_generic_args(
                StructUsedAndUnusedGenericParams::new(10u8),
                EnumUsedAndUnusedGenericParams::Two(11u8),
            )
            .call()
            .await?
            .value;

        assert_eq!(the_struct.field, 12u8);
        if let EnumUsedAndUnusedGenericParams::Two(val) = the_enum {
            assert_eq!(val, 13)
        } else {
            panic!("Expected the variant EnumUsedAndUnusedGenericParams::Two");
        }
    }
    {
        // complex case
        let pass_through = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "ab".try_into()?,
            },
        };
        let w_arr_generic = StructWArrayGeneric {
            a: [pass_through.clone(), pass_through],
        };

        let arg1 = MegaExample {
            a: ([Bits256([0; 32]), Bits256([0; 32])], "ab".try_into()?),
            b: vec![(
                [EnumWGeneric::B(StructWTupleGeneric {
                    a: (w_arr_generic.clone(), w_arr_generic),
                })],
                10u32,
            )],
        };
        contract_methods.complex_test(arg1.clone()).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_vectors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/vectors"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let methods = contract_instance.methods();

    {
        // vec of u32s
        let arg = vec![0, 1, 2];
        methods.u32_vec(arg).call().await?;
    }
    {
        // vec of vecs of u32s
        let arg = vec![vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_vec(arg.clone()).call().await?;
    }
    {
        // vec of structs
        // ANCHOR: passing_in_vec
        let arg = vec![SomeStruct { a: 0 }, SomeStruct { a: 1 }];
        methods.struct_in_vec(arg.clone()).call().await?;
        // ANCHOR_END: passing_in_vec
    }
    {
        // vec in struct
        let arg = SomeStruct { a: vec![0, 1, 2] };
        methods.vec_in_struct(arg.clone()).call().await?;
    }
    {
        // array in vec
        let arg = vec![[0u64, 1u64], [0u64, 1u64]];
        methods.array_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in array
        let arg = [vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_array(arg.clone()).call().await?;
    }
    {
        // vec in enum
        let arg = SomeEnum::a(vec![0, 1, 2]);
        methods.vec_in_enum(arg.clone()).call().await?;
    }
    {
        // enum in vec
        let arg = vec![SomeEnum::a(0), SomeEnum::a(1)];
        methods.enum_in_vec(arg.clone()).call().await?;
    }
    {
        // tuple in vec
        let arg = vec![(0, 0), (1, 1)];
        methods.tuple_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in tuple
        let arg = (vec![0, 1, 2], vec![0, 1, 2]);
        methods.vec_in_tuple(arg.clone()).call().await?;
    }
    {
        // vec in a vec in a struct in a vec
        let arg = vec![
            SomeStruct {
                a: vec![vec![0, 1, 2], vec![3, 4, 5]],
            },
            SomeStruct {
                a: vec![vec![6, 7, 8], vec![9, 10, 11]],
            },
        ];
        methods
            .vec_in_a_vec_in_a_struct_in_a_vec(arg.clone())
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_b256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        Bits256([2; 32]),
        contract_instance
            .methods()
            .b256_as_output()
            .call()
            .await?
            .value
    );

    {
        // ANCHOR: 256_arg
        let b256 = Bits256([1; 32]);

        let call_handler = contract_instance.methods().b256_as_input(b256);
        // ANCHOR_END: 256_arg

        assert!(call_handler.call().await?.value);
    }

    Ok(())
}

#[tokio::test]
async fn test_b512() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b512"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: b512_example
    let hi_bits = Bits256::from_hex_str(
        "0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c",
    )?;
    let lo_bits = Bits256::from_hex_str(
        "0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
    )?;
    let b512 = B512::from((hi_bits, lo_bits));
    // ANCHOR_END: b512_example

    assert_eq!(b512, contract_methods.b512_as_output().call().await?.value);

    {
        let lo_bits2 = Bits256::from_hex_str(
            "0x54ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
        )?;
        let b512 = B512::from((hi_bits, lo_bits2));

        assert!(contract_methods.b512_as_input(b512).call().await?.value);
    }

    Ok(())
}

fn u128_from(parts: (u64, u64)) -> u128 {
    let bytes: [u8; 16] = [parts.0.to_be_bytes(), parts.1.to_be_bytes()]
        .concat()
        .try_into()
        .unwrap();
    u128::from_be_bytes(bytes)
}

#[tokio::test]
async fn test_u128() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u128"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u128_from((1, 2));

        let actual = contract_methods.u128_sum_and_ret(arg).call().await?.value;

        let expected = arg + u128_from((3, 4));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u128_in_enum_output().call().await?.value;

        let expected = SomeEnum::B(u128_from((4, 4)));
        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u128_from((3, 3)));

        contract_methods.u128_in_enum_input(input).call().await?;
    }

    Ok(())
}

fn u256_from(parts: (u64, u64, u64, u64)) -> U256 {
    let bytes: [u8; 32] = [
        parts.0.to_be_bytes(),
        parts.1.to_be_bytes(),
        parts.2.to_be_bytes(),
        parts.3.to_be_bytes(),
    ]
    .concat()
    .try_into()
    .unwrap();
    U256::from(bytes)
}

#[tokio::test]
async fn test_u256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u256_from((1, 2, 3, 4));
        let actual = contract_methods.u256_sum_and_ret(arg).call().await?.value;
        let expected = arg + u256_from((3, 4, 5, 6));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u256_in_enum_output().call().await?.value;
        let expected = SomeEnum::B(u256_from((1, 2, 3, 4)));

        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u256_from((2, 3, 4, 5)));

        contract_methods.u256_in_enum_input(input).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_base_type_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: returning_vec
    let response = contract_methods.u8_in_vec(10).call().await?;
    assert_eq!(response.value, (0..10).collect::<Vec<_>>());
    // ANCHOR_END: returning_vec

    let response = contract_methods.u16_in_vec(11).call().await?;
    assert_eq!(response.value, (0..11).collect::<Vec<_>>());

    let response = contract_methods.u32_in_vec(12).call().await?;
    assert_eq!(response.value, (0..12).collect::<Vec<_>>());

    let response = contract_methods.u64_in_vec(13).call().await?;
    assert_eq!(response.value, (0..13).collect::<Vec<_>>());

    let response = contract_methods.bool_in_vec().call().await?;
    assert_eq!(response.value, [true, false, true, false].to_vec());

    let response = contract_methods.b256_in_vec(13).call().await?;
    assert_eq!(response.value, vec![Bits256([2; 32]); 13]);

    Ok(())
}

#[tokio::test]
async fn test_composite_types_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let expected: Vec<[u64; 4]> = vec![[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
        let response = contract_methods.array_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    {
        let expected: Vec<Pasta> = vec![
            Pasta::Tortelini(Bimbam {
                bim: 1111,
                bam: 2222_u32,
            }),
            Pasta::Rigatoni(1987),
            Pasta::Spaghetti(true),
        ];

        let response = contract_methods.enum_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<Bimbam> = vec![
            Bimbam {
                bim: 1111,
                bam: 2222_u32,
            },
            Bimbam {
                bim: 3333,
                bam: 4444_u32,
            },
            Bimbam {
                bim: 5555,
                bam: 6666_u32,
            },
        ];
        let response = contract_methods.struct_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<(u64, u32)> = vec![(1111, 2222_u32), (3333, 4444_u32), (5555, 6666_u32)];
        let response = contract_methods.tuple_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<SizedAsciiString<4>> =
            vec!["hell".try_into()?, "ello".try_into()?, "lloh".try_into()?];
        let response = contract_methods.str_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    Ok(())
}

#[tokio::test]
async fn test_bytes_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesOutputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.return_bytes(10).call().await?;

    assert_eq!(response.value, (0..10).collect::<Vec<_>>());

    Ok(())
}

#[tokio::test]
async fn test_bytes_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesInputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesInputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: bytes_arg
        let bytes = Bytes(vec![40, 41, 42]);

        contract_methods.accept_bytes(bytes).call().await?;
        // ANCHOR_END: bytes_arg
    }
    {
        let bytes = Bytes(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![bytes.clone(), bytes.clone()],
            inner_enum: SomeEnum::Second(bytes),
        };

        contract_methods.accept_nested_bytes(wrapper).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_raw_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RawSliceContract",
            project = "e2e/sway/types/contracts/raw_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RawSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    {
        for length in 0u8..=10 {
            let response = contract_methods.return_raw_slice(length).call().await?;
            assert_eq!(response.value, (0u8..length).collect::<Vec<u8>>());
        }
    }
    {
        contract_methods
            .accept_raw_slice(RawSlice(vec![40, 41, 42]))
            .call()
            .await?;
    }
    {
        let raw_slice = RawSlice(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![raw_slice.clone(), raw_slice.clone()],
            inner_enum: SomeEnum::Second(raw_slice),
        };

        contract_methods
            .accept_nested_raw_slice(wrapper)
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_string_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StringSliceContract",
            project = "e2e/sway/types/contracts/string_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StringSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .handles_str("contract-input".try_into()?)
        .call()
        .await?;
    assert_eq!(response.value, "contract-return");

    Ok(())
}

#[tokio::test]
async fn contract_std_lib_string() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StdLibString",
            project = "e2e/sway/types/contracts/std_lib_string"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StdLibString",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.return_dynamic_string().call().await?.value;
        assert_eq!(resp, "Hello World");
    }
    {
        let _resp = contract_methods
            .accepts_dynamic_string(String::from("Hello World"))
            .call()
            .await?;
    }
    {
        // confirm encoding/decoding a string wasn't faulty and led to too high gas consumption
        let _resp = contract_methods
            .echoes_dynamic_string(String::from("Hello Fuel"))
            .with_tx_policies(TxPolicies::default().with_script_gas_limit(3600))
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_heap_type_in_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_type_in_enums"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.returns_bytes_result(true).call().await?;
        let expected = Ok(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_result(false).call().await?;
        let expected = Err(TestError::Something([255u8, 255u8, 255u8, 255u8, 255u8]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(true).call().await?;
        let expected = Ok(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(false).call().await?;
        let expected = Err(TestError::Else(7777));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(true).call().await?;
        let expected = Ok("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_str_result(true).call().await?;
        let expected = Ok("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(true).call().await?;
        let expected = Some(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_vec_option(true).call().await?;
        let expected = Some(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_string_option(true).call().await?;
        let expected = Some("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_str_option(true).call().await?;
        let expected = Some("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }

    Ok(())
}

#[tokio::test]
async fn nested_heap_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let arr = [2u8, 4, 8];
    let struct_generics = StructGenerics {
        one: Bytes(arr.to_vec()),
        two: String::from("fuel"),
        three: RawSlice(arr.to_vec()),
    };

    let enum_vec = [struct_generics.clone(), struct_generics].to_vec();
    let expected = EnumGeneric::One(enum_vec);

    let result = contract_instance
        .methods()
        .nested_heap_types()
        .call()
        .await?;

    assert_eq!(result.value, expected);

    Ok(())
}\n```

Note: when creating an EvmAddress from Bits256, the first 12 bytes will be cleared because an EVM address is only 20 bytes long.

Vectors

Passing in vectors

You can pass a Rust std::vec::Vec into your contract method transparently. The following code calls a Sway contract method which accepts a Vec<SomeStruct<u32>>.

```rust\nuse std::str::FromStr;

use fuels::{
    prelude::*,
    types::{Bits256, EvmAddress, Identity, SizedAsciiString, B512, U256},
};

pub fn null_contract_id() -> Bech32ContractId {
    // a bech32 contract address that decodes to [0u8;32]
    Bech32ContractId::from_str("fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2")
        .expect("is valid")
}

#[tokio::test]
async fn test_methods_typeless_argument() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/empty_arguments"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .method_with_empty_argument()
        .call()
        .await?;

    assert_eq!(response.value, 63);

    Ok(())
}

#[tokio::test]
async fn call_with_empty_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/types/contracts/call_empty_return"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let _response = contract_instance.methods().store_value(42).call().await?;
    Ok(())
}

#[tokio::test]
async fn call_with_structs() -> Result<()> {
    // Generates the bindings from the an ABI definition inline.
    // The generated bindings can be accessed through `MyContract`.
    // ANCHOR: struct_generation
    abigen!(Contract(name="MyContract",
                     abi="e2e/sway/types/contracts/complex_types_contract/out/release/complex_types_contract-abi.json"));

    // Here we can use `CounterConfig`, a struct originally
    // defined in the contract.
    let counter_config = CounterConfig {
        dummy: true,
        initial_value: 42,
    };
    // ANCHOR_END: struct_generation

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/types/contracts/complex_types_contract/out/release/complex_types_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id, wallet).methods();

    let response = contract_methods
        .initialize_counter(counter_config)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let response = contract_methods.increment_counter(10).call().await?;

    assert_eq!(52, response.value);

    Ok(())
}

#[tokio::test]
async fn abigen_different_structs_same_arg_name() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/two_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let param_one = StructOne { foo: 42 };
    let param_two = StructTwo { bar: 42 };

    let contract_methods = contract_instance.methods();
    let res_one = contract_methods.something(param_one).call().await?;

    assert_eq!(res_one.value, 43);

    let res_two = contract_methods.something_else(param_two).call().await?;

    assert_eq!(res_two.value, 41);
    Ok(())
}

#[tokio::test]
async fn nested_structs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/nested_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = AllStruct {
        some_struct: SomeStruct {
            field: 12345,
            field_2: true,
        },
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_struct().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_struct_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the argument correctly. Investigate!"
    );

    let memory_address = MemoryAddress {
        contract_id: ContractId::zeroed(),
        function_selector: 10,
        function_data: 0,
    };

    let call_data = CallData {
        memory_address,
        num_coins_to_forward: 10,
        asset_id_of_coins_to_forward: ContractId::zeroed(),
        amount_of_gas_to_forward: 5,
    };

    let actual = contract_methods
        .nested_struct_with_reserved_keyword_substring(call_data.clone())
        .call()
        .await?
        .value;

    assert_eq!(actual, call_data);
    Ok(())
}

#[tokio::test]
async fn calls_with_empty_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/complex_types_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.get_empty_struct().call().await?;

        assert_eq!(response.value, EmptyStruct {});
    }
    {
        let response = contract_methods
            .input_empty_struct(EmptyStruct {})
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_struct_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let cocktail_in_bytes: Vec<u8> = vec![
        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3,
    ];

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(2),
        glass: 3,
    };

    // as slice
    let actual: Cocktail = cocktail_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Cocktail = (&cocktail_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Cocktail = cocktail_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn test_tuples() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/tuples"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.returns_tuple((1, 2)).call().await?;

        assert_eq!(response.value, (1, 2));
    }
    {
        // Tuple with struct.
        let my_struct_tuple = (
            42,
            Person {
                name: "Jane".try_into()?,
            },
        );
        let response = contract_methods
            .returns_struct_in_tuple(my_struct_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_struct_tuple);
    }
    {
        // Tuple with enum.
        let my_enum_tuple: (u64, State) = (42, State::A);

        let response = contract_methods
            .returns_enum_in_tuple(my_enum_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // Tuple with single element
        let my_enum_tuple = (123u64,);

        let response = contract_methods
            .single_element_tuple(my_enum_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // tuple with b256
        let id = *ContractId::zeroed();
        let my_b256_u8_tuple = (Bits256(id), 10);

        let response = contract_methods
            .tuple_with_b256(my_b256_u8_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_b256_u8_tuple);
    }

    Ok(())
}

#[tokio::test]
async fn test_evm_address() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/evm_address"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        // ANCHOR: evm_address_arg
        let b256 = Bits256::from_hex_str(
            "0x1616060606060606060606060606060606060606060606060606060606060606",
        )?;
        let evm_address = EvmAddress::from(b256);

        let call_handler = contract_instance
            .methods()
            .evm_address_as_input(evm_address);
        // ANCHOR_END: evm_address_arg

        assert!(call_handler.call().await?.value);
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_literal()
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_argument(b256)
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        contract_instance
            .methods()
            .get_array([42; 2])
            .call()
            .await?
            .value,
        [42; 2]
    );
    Ok(())
}

#[tokio::test]
async fn test_arrays_with_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let persons = [
        Person {
            name: "John".try_into()?,
        },
        Person {
            name: "Jane".try_into()?,
        },
    ];

    let contract_methods = contract_instance.methods();
    let response = contract_methods.array_of_structs(persons).call().await?;

    assert_eq!("John", response.value[0].name);
    assert_eq!("Jane", response.value[1].name);

    let states = [State::A, State::B];

    let response = contract_methods
        .array_of_enums(states.clone())
        .call()
        .await?;

    assert_eq!(states[0], response.value[0]);
    assert_eq!(states[1], response.value[1]);
    Ok(())
}

#[tokio::test]
async fn str_in_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/str_in_array"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let input = ["foo", "bar", "baz"].map(|str| str.try_into().unwrap());
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .take_array_string_shuffle(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["baz", "foo", "bar"]);

    let response = contract_methods
        .take_array_string_return_single(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["foo"]);

    let response = contract_methods
        .take_array_string_return_single_element(input)
        .call()
        .await?;

    assert_eq!(response.value, "bar");
    Ok(())
}

#[tokio::test]
async fn test_enum_inside_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_inside_struct"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(11),
        glass: 333,
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .return_enum_inside_struct(11)
        .call()
        .await?;

    assert_eq!(response.value, expected);

    let enum_inside_struct = Cocktail {
        the_thing_you_mix_in: Shaker::Cosmopolitan(444),
        glass: 555,
    };

    let response = contract_methods
        .take_enum_inside_struct(enum_inside_struct)
        .call()
        .await?;

    assert_eq!(response.value, 555);
    Ok(())
}

#[tokio::test]
async fn native_types_support() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/native_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let user = User {
        weight: 10,
        address: Address::zeroed(),
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods.wrapped_address(user).call().await?;

    assert_eq!(response.value.address, Address::zeroed());

    let response = contract_methods
        .unwrapped_address(Address::zeroed())
        .call()
        .await?;

    assert_eq!(
        response.value,
        Address::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")?
    );

    Ok(())
}

#[tokio::test]
async fn enum_coding_w_variable_width_variants() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of enum encoding width, then we'll
    // probably end up mangling arg_2 and onward which will fail this test.
    let expected = BigBundle {
        arg_1: EnumThatHasABigAndSmallVariant::Small(12345),
        arg_2: 6666,
        arg_3: 7777,
        arg_4: 8888,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_big_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_big_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_coding_w_unit_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of unit enum encoding width, then
    // we'll end up mangling arg_2
    let expected = UnitBundle {
        arg_1: UnitEnum::var2,
        arg_2: u64::MAX,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_unit_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_as_input"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = MaxedOutVariantsEnum::Variant255(11);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_max_variant().call().await?.value;
    assert_eq!(expected, actual);

    let expected = StandardEnum::Two(12345);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_standard_enum().call().await?.value;
    assert_eq!(expected, actual);

    let fuelvm_judgement = contract_methods
        .check_standard_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the standard enum correctly. Investigate!"
    );

    let expected = UnitEnum::Two;
    let actual = contract_methods.get_unit_enum().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the unit enum correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_enum_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let shaker_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];

    let expected = Shaker::Mojito(2);

    // as slice
    let actual: Shaker = shaker_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Shaker = (&shaker_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Shaker = shaker_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn type_inside_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/type_inside_enum"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // String inside enum
    let enum_string = SomeEnum::SomeStr("asdf".try_into()?);
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .str_inside_enum(enum_string.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_string);

    // Array inside enum
    let enum_array = SomeEnum::SomeArr([1, 2, 3, 4]);
    let response = contract_methods
        .arr_inside_enum(enum_array.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_array);

    // Struct inside enum
    let response = contract_methods
        .return_struct_inside_enum(11)
        .call()
        .await?;
    let expected = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 11 });
    assert_eq!(response.value, expected);

    let struct_inside_enum = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 66 });
    let response = contract_methods
        .take_struct_inside_enum(struct_inside_enum)
        .call()
        .await?;
    assert_eq!(response.value, 8888);

    // Enum inside enum
    let expected_enum = EnumLevel3::El2(EnumLevel2::El1(EnumLevel1::Num(42)));
    let response = contract_methods.get_nested_enum().call().await?;
    assert_eq!(response.value, expected_enum);

    let response = contract_methods
        .check_nested_enum_integrity(expected_enum)
        .call()
        .await?;
    assert!(
        response.value,
        "The FuelVM deems that we've not encoded the nested enum correctly. Investigate!"
    );

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_some_address = Some(expected_address);
    let response = contract_methods.get_some_address().call().await?;

    assert_eq!(response.value, expected_some_address);

    let expected_some_u64 = Some(10);
    let response = contract_methods.get_some_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_some_struct().call().await?;
    assert_eq!(response.value, Some(s.clone()));

    let response = contract_methods.get_some_enum().call().await?;
    assert_eq!(response.value, Some(e.clone()));

    let response = contract_methods.get_some_tuple().call().await?;
    assert_eq!(response.value, Some((s.clone(), e.clone())));

    let expected_none = None;
    let response = contract_methods.get_none().call().await?;

    assert_eq!(response.value, expected_none);

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_u64 = Some(36);
    let response = contract_methods
        .input_primitive(expected_u64)
        .call()
        .await?;

    assert!(response.value);

    let expected_struct = Some(s);
    let response = contract_methods
        .input_struct(expected_struct)
        .call()
        .await?;

    assert!(response.value);

    let expected_enum = Some(e);
    let response = contract_methods.input_enum(expected_enum).call().await?;

    assert!(response.value);

    let expected_none = None;
    let response = contract_methods.input_none(expected_none).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods.get_ok_address().call().await?;

    assert_eq!(response.value, expected_ok_address);

    let expected_some_u64 = Ok(10);
    let response = contract_methods.get_ok_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_ok_struct().call().await?;
    assert_eq!(response.value, Ok(s.clone()));

    let response = contract_methods.get_ok_enum().call().await?;
    assert_eq!(response.value, Ok(e.clone()));

    let response = contract_methods.get_ok_tuple().call().await?;
    assert_eq!(response.value, Ok((s, e)));

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.get_error().call().await?;

    assert_eq!(response.value, expected_error);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods
        .input_ok(expected_ok_address)
        .call()
        .await?;

    assert!(response.value);

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.input_error(expected_error).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_identity_address().call().await?;
    assert_eq!(response.value, Identity::Address(expected_address));

    let response = contract_methods.get_identity_contract_id().call().await?;
    assert_eq!(response.value, Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_struct_with_identity().call().await?;
    assert_eq!(response.value, s.clone());

    let response = contract_methods.get_enum_with_identity().call().await?;
    assert_eq!(response.value, e.clone());

    let response = contract_methods.get_identity_tuple().call().await?;
    assert_eq!(response.value, (s, e));

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods
        .input_identity(Identity::Address(expected_address))
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods
        .input_struct_with_identity(s)
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods.input_enum_with_identity(e).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_with_two_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    {
        let response = contract_instance
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }
    {
        let response = contract_instance2
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn generics_test() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/generics"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: generic
        // simple struct with a single generic param
        let arg1 = SimpleGeneric {
            single_generic_param: 123u64,
        };

        let result = contract_methods
            .struct_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
        // ANCHOR_END: generic
    }
    {
        // struct that delegates the generic param internally
        let arg1 = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "abc".try_into()?,
            },
        };

        let result = contract_methods
            .struct_delegating_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in an array
        let arg1 = StructWArrayGeneric { a: [1u32, 2u32] };

        let result = contract_methods
            .struct_w_generic_in_array(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has a generic struct in an array
        let inner = [
            StructWTwoGenerics {
                a: Bits256([1u8; 32]),
                b: 1,
            },
            StructWTwoGenerics {
                a: Bits256([2u8; 32]),
                b: 2,
            },
            StructWTwoGenerics {
                a: Bits256([3u8; 32]),
                b: 3,
            },
        ];
        let arg1 = StructWArrWGenericStruct { a: inner };

        let result = contract_methods
            .array_with_generic_struct(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in a tuple
        let arg1 = StructWTupleGeneric { a: (1, 2) };

        let result = contract_methods
            .struct_w_generic_in_tuple(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // enum with generic in variant
        let arg1 = EnumWGeneric::B(10);
        let result = contract_methods
            .enum_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        contract_methods
            .unused_generic_args(StructUnusedGeneric::new(15), EnumUnusedGeneric::One(15))
            .call()
            .await?;

        let (the_struct, the_enum) = contract_methods
            .used_and_unused_generic_args(
                StructUsedAndUnusedGenericParams::new(10u8),
                EnumUsedAndUnusedGenericParams::Two(11u8),
            )
            .call()
            .await?
            .value;

        assert_eq!(the_struct.field, 12u8);
        if let EnumUsedAndUnusedGenericParams::Two(val) = the_enum {
            assert_eq!(val, 13)
        } else {
            panic!("Expected the variant EnumUsedAndUnusedGenericParams::Two");
        }
    }
    {
        // complex case
        let pass_through = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "ab".try_into()?,
            },
        };
        let w_arr_generic = StructWArrayGeneric {
            a: [pass_through.clone(), pass_through],
        };

        let arg1 = MegaExample {
            a: ([Bits256([0; 32]), Bits256([0; 32])], "ab".try_into()?),
            b: vec![(
                [EnumWGeneric::B(StructWTupleGeneric {
                    a: (w_arr_generic.clone(), w_arr_generic),
                })],
                10u32,
            )],
        };
        contract_methods.complex_test(arg1.clone()).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_vectors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/vectors"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let methods = contract_instance.methods();

    {
        // vec of u32s
        let arg = vec![0, 1, 2];
        methods.u32_vec(arg).call().await?;
    }
    {
        // vec of vecs of u32s
        let arg = vec![vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_vec(arg.clone()).call().await?;
    }
    {
        // vec of structs
        // ANCHOR: passing_in_vec
        let arg = vec![SomeStruct { a: 0 }, SomeStruct { a: 1 }];
        methods.struct_in_vec(arg.clone()).call().await?;
        // ANCHOR_END: passing_in_vec
    }
    {
        // vec in struct
        let arg = SomeStruct { a: vec![0, 1, 2] };
        methods.vec_in_struct(arg.clone()).call().await?;
    }
    {
        // array in vec
        let arg = vec![[0u64, 1u64], [0u64, 1u64]];
        methods.array_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in array
        let arg = [vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_array(arg.clone()).call().await?;
    }
    {
        // vec in enum
        let arg = SomeEnum::a(vec![0, 1, 2]);
        methods.vec_in_enum(arg.clone()).call().await?;
    }
    {
        // enum in vec
        let arg = vec![SomeEnum::a(0), SomeEnum::a(1)];
        methods.enum_in_vec(arg.clone()).call().await?;
    }
    {
        // tuple in vec
        let arg = vec![(0, 0), (1, 1)];
        methods.tuple_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in tuple
        let arg = (vec![0, 1, 2], vec![0, 1, 2]);
        methods.vec_in_tuple(arg.clone()).call().await?;
    }
    {
        // vec in a vec in a struct in a vec
        let arg = vec![
            SomeStruct {
                a: vec![vec![0, 1, 2], vec![3, 4, 5]],
            },
            SomeStruct {
                a: vec![vec![6, 7, 8], vec![9, 10, 11]],
            },
        ];
        methods
            .vec_in_a_vec_in_a_struct_in_a_vec(arg.clone())
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_b256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        Bits256([2; 32]),
        contract_instance
            .methods()
            .b256_as_output()
            .call()
            .await?
            .value
    );

    {
        // ANCHOR: 256_arg
        let b256 = Bits256([1; 32]);

        let call_handler = contract_instance.methods().b256_as_input(b256);
        // ANCHOR_END: 256_arg

        assert!(call_handler.call().await?.value);
    }

    Ok(())
}

#[tokio::test]
async fn test_b512() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b512"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: b512_example
    let hi_bits = Bits256::from_hex_str(
        "0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c",
    )?;
    let lo_bits = Bits256::from_hex_str(
        "0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
    )?;
    let b512 = B512::from((hi_bits, lo_bits));
    // ANCHOR_END: b512_example

    assert_eq!(b512, contract_methods.b512_as_output().call().await?.value);

    {
        let lo_bits2 = Bits256::from_hex_str(
            "0x54ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
        )?;
        let b512 = B512::from((hi_bits, lo_bits2));

        assert!(contract_methods.b512_as_input(b512).call().await?.value);
    }

    Ok(())
}

fn u128_from(parts: (u64, u64)) -> u128 {
    let bytes: [u8; 16] = [parts.0.to_be_bytes(), parts.1.to_be_bytes()]
        .concat()
        .try_into()
        .unwrap();
    u128::from_be_bytes(bytes)
}

#[tokio::test]
async fn test_u128() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u128"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u128_from((1, 2));

        let actual = contract_methods.u128_sum_and_ret(arg).call().await?.value;

        let expected = arg + u128_from((3, 4));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u128_in_enum_output().call().await?.value;

        let expected = SomeEnum::B(u128_from((4, 4)));
        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u128_from((3, 3)));

        contract_methods.u128_in_enum_input(input).call().await?;
    }

    Ok(())
}

fn u256_from(parts: (u64, u64, u64, u64)) -> U256 {
    let bytes: [u8; 32] = [
        parts.0.to_be_bytes(),
        parts.1.to_be_bytes(),
        parts.2.to_be_bytes(),
        parts.3.to_be_bytes(),
    ]
    .concat()
    .try_into()
    .unwrap();
    U256::from(bytes)
}

#[tokio::test]
async fn test_u256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u256_from((1, 2, 3, 4));
        let actual = contract_methods.u256_sum_and_ret(arg).call().await?.value;
        let expected = arg + u256_from((3, 4, 5, 6));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u256_in_enum_output().call().await?.value;
        let expected = SomeEnum::B(u256_from((1, 2, 3, 4)));

        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u256_from((2, 3, 4, 5)));

        contract_methods.u256_in_enum_input(input).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_base_type_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: returning_vec
    let response = contract_methods.u8_in_vec(10).call().await?;
    assert_eq!(response.value, (0..10).collect::<Vec<_>>());
    // ANCHOR_END: returning_vec

    let response = contract_methods.u16_in_vec(11).call().await?;
    assert_eq!(response.value, (0..11).collect::<Vec<_>>());

    let response = contract_methods.u32_in_vec(12).call().await?;
    assert_eq!(response.value, (0..12).collect::<Vec<_>>());

    let response = contract_methods.u64_in_vec(13).call().await?;
    assert_eq!(response.value, (0..13).collect::<Vec<_>>());

    let response = contract_methods.bool_in_vec().call().await?;
    assert_eq!(response.value, [true, false, true, false].to_vec());

    let response = contract_methods.b256_in_vec(13).call().await?;
    assert_eq!(response.value, vec![Bits256([2; 32]); 13]);

    Ok(())
}

#[tokio::test]
async fn test_composite_types_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let expected: Vec<[u64; 4]> = vec![[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
        let response = contract_methods.array_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    {
        let expected: Vec<Pasta> = vec![
            Pasta::Tortelini(Bimbam {
                bim: 1111,
                bam: 2222_u32,
            }),
            Pasta::Rigatoni(1987),
            Pasta::Spaghetti(true),
        ];

        let response = contract_methods.enum_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<Bimbam> = vec![
            Bimbam {
                bim: 1111,
                bam: 2222_u32,
            },
            Bimbam {
                bim: 3333,
                bam: 4444_u32,
            },
            Bimbam {
                bim: 5555,
                bam: 6666_u32,
            },
        ];
        let response = contract_methods.struct_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<(u64, u32)> = vec![(1111, 2222_u32), (3333, 4444_u32), (5555, 6666_u32)];
        let response = contract_methods.tuple_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<SizedAsciiString<4>> =
            vec!["hell".try_into()?, "ello".try_into()?, "lloh".try_into()?];
        let response = contract_methods.str_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    Ok(())
}

#[tokio::test]
async fn test_bytes_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesOutputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.return_bytes(10).call().await?;

    assert_eq!(response.value, (0..10).collect::<Vec<_>>());

    Ok(())
}

#[tokio::test]
async fn test_bytes_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesInputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesInputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: bytes_arg
        let bytes = Bytes(vec![40, 41, 42]);

        contract_methods.accept_bytes(bytes).call().await?;
        // ANCHOR_END: bytes_arg
    }
    {
        let bytes = Bytes(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![bytes.clone(), bytes.clone()],
            inner_enum: SomeEnum::Second(bytes),
        };

        contract_methods.accept_nested_bytes(wrapper).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_raw_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RawSliceContract",
            project = "e2e/sway/types/contracts/raw_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RawSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    {
        for length in 0u8..=10 {
            let response = contract_methods.return_raw_slice(length).call().await?;
            assert_eq!(response.value, (0u8..length).collect::<Vec<u8>>());
        }
    }
    {
        contract_methods
            .accept_raw_slice(RawSlice(vec![40, 41, 42]))
            .call()
            .await?;
    }
    {
        let raw_slice = RawSlice(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![raw_slice.clone(), raw_slice.clone()],
            inner_enum: SomeEnum::Second(raw_slice),
        };

        contract_methods
            .accept_nested_raw_slice(wrapper)
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_string_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StringSliceContract",
            project = "e2e/sway/types/contracts/string_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StringSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .handles_str("contract-input".try_into()?)
        .call()
        .await?;
    assert_eq!(response.value, "contract-return");

    Ok(())
}

#[tokio::test]
async fn contract_std_lib_string() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StdLibString",
            project = "e2e/sway/types/contracts/std_lib_string"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StdLibString",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.return_dynamic_string().call().await?.value;
        assert_eq!(resp, "Hello World");
    }
    {
        let _resp = contract_methods
            .accepts_dynamic_string(String::from("Hello World"))
            .call()
            .await?;
    }
    {
        // confirm encoding/decoding a string wasn't faulty and led to too high gas consumption
        let _resp = contract_methods
            .echoes_dynamic_string(String::from("Hello Fuel"))
            .with_tx_policies(TxPolicies::default().with_script_gas_limit(3600))
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_heap_type_in_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_type_in_enums"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.returns_bytes_result(true).call().await?;
        let expected = Ok(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_result(false).call().await?;
        let expected = Err(TestError::Something([255u8, 255u8, 255u8, 255u8, 255u8]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(true).call().await?;
        let expected = Ok(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(false).call().await?;
        let expected = Err(TestError::Else(7777));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(true).call().await?;
        let expected = Ok("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_str_result(true).call().await?;
        let expected = Ok("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(true).call().await?;
        let expected = Some(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_vec_option(true).call().await?;
        let expected = Some(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_string_option(true).call().await?;
        let expected = Some("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_str_option(true).call().await?;
        let expected = Some("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }

    Ok(())
}

#[tokio::test]
async fn nested_heap_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let arr = [2u8, 4, 8];
    let struct_generics = StructGenerics {
        one: Bytes(arr.to_vec()),
        two: String::from("fuel"),
        three: RawSlice(arr.to_vec()),
    };

    let enum_vec = [struct_generics.clone(), struct_generics].to_vec();
    let expected = EnumGeneric::One(enum_vec);

    let result = contract_instance
        .methods()
        .nested_heap_types()
        .call()
        .await?;

    assert_eq!(result.value, expected);

    Ok(())
}\n```

You can use a vector just like you would use any other type -- e.g. a [Vec<u32>; 2] or a SomeStruct<Vec<Bits256>> etc.

Returning vectors

Returning vectors from contract methods is supported transparently, with the caveat that you cannot have them nested inside another type. This limitation is temporary.

```rust\nuse std::str::FromStr;

use fuels::{
    prelude::*,
    types::{Bits256, EvmAddress, Identity, SizedAsciiString, B512, U256},
};

pub fn null_contract_id() -> Bech32ContractId {
    // a bech32 contract address that decodes to [0u8;32]
    Bech32ContractId::from_str("fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2")
        .expect("is valid")
}

#[tokio::test]
async fn test_methods_typeless_argument() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/empty_arguments"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .method_with_empty_argument()
        .call()
        .await?;

    assert_eq!(response.value, 63);

    Ok(())
}

#[tokio::test]
async fn call_with_empty_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/types/contracts/call_empty_return"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let _response = contract_instance.methods().store_value(42).call().await?;
    Ok(())
}

#[tokio::test]
async fn call_with_structs() -> Result<()> {
    // Generates the bindings from the an ABI definition inline.
    // The generated bindings can be accessed through `MyContract`.
    // ANCHOR: struct_generation
    abigen!(Contract(name="MyContract",
                     abi="e2e/sway/types/contracts/complex_types_contract/out/release/complex_types_contract-abi.json"));

    // Here we can use `CounterConfig`, a struct originally
    // defined in the contract.
    let counter_config = CounterConfig {
        dummy: true,
        initial_value: 42,
    };
    // ANCHOR_END: struct_generation

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/types/contracts/complex_types_contract/out/release/complex_types_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id, wallet).methods();

    let response = contract_methods
        .initialize_counter(counter_config)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let response = contract_methods.increment_counter(10).call().await?;

    assert_eq!(52, response.value);

    Ok(())
}

#[tokio::test]
async fn abigen_different_structs_same_arg_name() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/two_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let param_one = StructOne { foo: 42 };
    let param_two = StructTwo { bar: 42 };

    let contract_methods = contract_instance.methods();
    let res_one = contract_methods.something(param_one).call().await?;

    assert_eq!(res_one.value, 43);

    let res_two = contract_methods.something_else(param_two).call().await?;

    assert_eq!(res_two.value, 41);
    Ok(())
}

#[tokio::test]
async fn nested_structs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/nested_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = AllStruct {
        some_struct: SomeStruct {
            field: 12345,
            field_2: true,
        },
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_struct().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_struct_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the argument correctly. Investigate!"
    );

    let memory_address = MemoryAddress {
        contract_id: ContractId::zeroed(),
        function_selector: 10,
        function_data: 0,
    };

    let call_data = CallData {
        memory_address,
        num_coins_to_forward: 10,
        asset_id_of_coins_to_forward: ContractId::zeroed(),
        amount_of_gas_to_forward: 5,
    };

    let actual = contract_methods
        .nested_struct_with_reserved_keyword_substring(call_data.clone())
        .call()
        .await?
        .value;

    assert_eq!(actual, call_data);
    Ok(())
}

#[tokio::test]
async fn calls_with_empty_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/complex_types_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.get_empty_struct().call().await?;

        assert_eq!(response.value, EmptyStruct {});
    }
    {
        let response = contract_methods
            .input_empty_struct(EmptyStruct {})
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_struct_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let cocktail_in_bytes: Vec<u8> = vec![
        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3,
    ];

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(2),
        glass: 3,
    };

    // as slice
    let actual: Cocktail = cocktail_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Cocktail = (&cocktail_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Cocktail = cocktail_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn test_tuples() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/tuples"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.returns_tuple((1, 2)).call().await?;

        assert_eq!(response.value, (1, 2));
    }
    {
        // Tuple with struct.
        let my_struct_tuple = (
            42,
            Person {
                name: "Jane".try_into()?,
            },
        );
        let response = contract_methods
            .returns_struct_in_tuple(my_struct_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_struct_tuple);
    }
    {
        // Tuple with enum.
        let my_enum_tuple: (u64, State) = (42, State::A);

        let response = contract_methods
            .returns_enum_in_tuple(my_enum_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // Tuple with single element
        let my_enum_tuple = (123u64,);

        let response = contract_methods
            .single_element_tuple(my_enum_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // tuple with b256
        let id = *ContractId::zeroed();
        let my_b256_u8_tuple = (Bits256(id), 10);

        let response = contract_methods
            .tuple_with_b256(my_b256_u8_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_b256_u8_tuple);
    }

    Ok(())
}

#[tokio::test]
async fn test_evm_address() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/evm_address"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        // ANCHOR: evm_address_arg
        let b256 = Bits256::from_hex_str(
            "0x1616060606060606060606060606060606060606060606060606060606060606",
        )?;
        let evm_address = EvmAddress::from(b256);

        let call_handler = contract_instance
            .methods()
            .evm_address_as_input(evm_address);
        // ANCHOR_END: evm_address_arg

        assert!(call_handler.call().await?.value);
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_literal()
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_argument(b256)
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        contract_instance
            .methods()
            .get_array([42; 2])
            .call()
            .await?
            .value,
        [42; 2]
    );
    Ok(())
}

#[tokio::test]
async fn test_arrays_with_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let persons = [
        Person {
            name: "John".try_into()?,
        },
        Person {
            name: "Jane".try_into()?,
        },
    ];

    let contract_methods = contract_instance.methods();
    let response = contract_methods.array_of_structs(persons).call().await?;

    assert_eq!("John", response.value[0].name);
    assert_eq!("Jane", response.value[1].name);

    let states = [State::A, State::B];

    let response = contract_methods
        .array_of_enums(states.clone())
        .call()
        .await?;

    assert_eq!(states[0], response.value[0]);
    assert_eq!(states[1], response.value[1]);
    Ok(())
}

#[tokio::test]
async fn str_in_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/str_in_array"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let input = ["foo", "bar", "baz"].map(|str| str.try_into().unwrap());
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .take_array_string_shuffle(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["baz", "foo", "bar"]);

    let response = contract_methods
        .take_array_string_return_single(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["foo"]);

    let response = contract_methods
        .take_array_string_return_single_element(input)
        .call()
        .await?;

    assert_eq!(response.value, "bar");
    Ok(())
}

#[tokio::test]
async fn test_enum_inside_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_inside_struct"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(11),
        glass: 333,
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .return_enum_inside_struct(11)
        .call()
        .await?;

    assert_eq!(response.value, expected);

    let enum_inside_struct = Cocktail {
        the_thing_you_mix_in: Shaker::Cosmopolitan(444),
        glass: 555,
    };

    let response = contract_methods
        .take_enum_inside_struct(enum_inside_struct)
        .call()
        .await?;

    assert_eq!(response.value, 555);
    Ok(())
}

#[tokio::test]
async fn native_types_support() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/native_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let user = User {
        weight: 10,
        address: Address::zeroed(),
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods.wrapped_address(user).call().await?;

    assert_eq!(response.value.address, Address::zeroed());

    let response = contract_methods
        .unwrapped_address(Address::zeroed())
        .call()
        .await?;

    assert_eq!(
        response.value,
        Address::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")?
    );

    Ok(())
}

#[tokio::test]
async fn enum_coding_w_variable_width_variants() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of enum encoding width, then we'll
    // probably end up mangling arg_2 and onward which will fail this test.
    let expected = BigBundle {
        arg_1: EnumThatHasABigAndSmallVariant::Small(12345),
        arg_2: 6666,
        arg_3: 7777,
        arg_4: 8888,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_big_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_big_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_coding_w_unit_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of unit enum encoding width, then
    // we'll end up mangling arg_2
    let expected = UnitBundle {
        arg_1: UnitEnum::var2,
        arg_2: u64::MAX,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_unit_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_as_input"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = MaxedOutVariantsEnum::Variant255(11);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_max_variant().call().await?.value;
    assert_eq!(expected, actual);

    let expected = StandardEnum::Two(12345);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_standard_enum().call().await?.value;
    assert_eq!(expected, actual);

    let fuelvm_judgement = contract_methods
        .check_standard_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the standard enum correctly. Investigate!"
    );

    let expected = UnitEnum::Two;
    let actual = contract_methods.get_unit_enum().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the unit enum correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_enum_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let shaker_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];

    let expected = Shaker::Mojito(2);

    // as slice
    let actual: Shaker = shaker_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Shaker = (&shaker_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Shaker = shaker_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn type_inside_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/type_inside_enum"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // String inside enum
    let enum_string = SomeEnum::SomeStr("asdf".try_into()?);
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .str_inside_enum(enum_string.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_string);

    // Array inside enum
    let enum_array = SomeEnum::SomeArr([1, 2, 3, 4]);
    let response = contract_methods
        .arr_inside_enum(enum_array.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_array);

    // Struct inside enum
    let response = contract_methods
        .return_struct_inside_enum(11)
        .call()
        .await?;
    let expected = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 11 });
    assert_eq!(response.value, expected);

    let struct_inside_enum = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 66 });
    let response = contract_methods
        .take_struct_inside_enum(struct_inside_enum)
        .call()
        .await?;
    assert_eq!(response.value, 8888);

    // Enum inside enum
    let expected_enum = EnumLevel3::El2(EnumLevel2::El1(EnumLevel1::Num(42)));
    let response = contract_methods.get_nested_enum().call().await?;
    assert_eq!(response.value, expected_enum);

    let response = contract_methods
        .check_nested_enum_integrity(expected_enum)
        .call()
        .await?;
    assert!(
        response.value,
        "The FuelVM deems that we've not encoded the nested enum correctly. Investigate!"
    );

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_some_address = Some(expected_address);
    let response = contract_methods.get_some_address().call().await?;

    assert_eq!(response.value, expected_some_address);

    let expected_some_u64 = Some(10);
    let response = contract_methods.get_some_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_some_struct().call().await?;
    assert_eq!(response.value, Some(s.clone()));

    let response = contract_methods.get_some_enum().call().await?;
    assert_eq!(response.value, Some(e.clone()));

    let response = contract_methods.get_some_tuple().call().await?;
    assert_eq!(response.value, Some((s.clone(), e.clone())));

    let expected_none = None;
    let response = contract_methods.get_none().call().await?;

    assert_eq!(response.value, expected_none);

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_u64 = Some(36);
    let response = contract_methods
        .input_primitive(expected_u64)
        .call()
        .await?;

    assert!(response.value);

    let expected_struct = Some(s);
    let response = contract_methods
        .input_struct(expected_struct)
        .call()
        .await?;

    assert!(response.value);

    let expected_enum = Some(e);
    let response = contract_methods.input_enum(expected_enum).call().await?;

    assert!(response.value);

    let expected_none = None;
    let response = contract_methods.input_none(expected_none).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods.get_ok_address().call().await?;

    assert_eq!(response.value, expected_ok_address);

    let expected_some_u64 = Ok(10);
    let response = contract_methods.get_ok_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_ok_struct().call().await?;
    assert_eq!(response.value, Ok(s.clone()));

    let response = contract_methods.get_ok_enum().call().await?;
    assert_eq!(response.value, Ok(e.clone()));

    let response = contract_methods.get_ok_tuple().call().await?;
    assert_eq!(response.value, Ok((s, e)));

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.get_error().call().await?;

    assert_eq!(response.value, expected_error);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods
        .input_ok(expected_ok_address)
        .call()
        .await?;

    assert!(response.value);

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.input_error(expected_error).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_identity_address().call().await?;
    assert_eq!(response.value, Identity::Address(expected_address));

    let response = contract_methods.get_identity_contract_id().call().await?;
    assert_eq!(response.value, Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_struct_with_identity().call().await?;
    assert_eq!(response.value, s.clone());

    let response = contract_methods.get_enum_with_identity().call().await?;
    assert_eq!(response.value, e.clone());

    let response = contract_methods.get_identity_tuple().call().await?;
    assert_eq!(response.value, (s, e));

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods
        .input_identity(Identity::Address(expected_address))
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods
        .input_struct_with_identity(s)
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods.input_enum_with_identity(e).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_with_two_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    {
        let response = contract_instance
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }
    {
        let response = contract_instance2
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn generics_test() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/generics"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: generic
        // simple struct with a single generic param
        let arg1 = SimpleGeneric {
            single_generic_param: 123u64,
        };

        let result = contract_methods
            .struct_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
        // ANCHOR_END: generic
    }
    {
        // struct that delegates the generic param internally
        let arg1 = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "abc".try_into()?,
            },
        };

        let result = contract_methods
            .struct_delegating_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in an array
        let arg1 = StructWArrayGeneric { a: [1u32, 2u32] };

        let result = contract_methods
            .struct_w_generic_in_array(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has a generic struct in an array
        let inner = [
            StructWTwoGenerics {
                a: Bits256([1u8; 32]),
                b: 1,
            },
            StructWTwoGenerics {
                a: Bits256([2u8; 32]),
                b: 2,
            },
            StructWTwoGenerics {
                a: Bits256([3u8; 32]),
                b: 3,
            },
        ];
        let arg1 = StructWArrWGenericStruct { a: inner };

        let result = contract_methods
            .array_with_generic_struct(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in a tuple
        let arg1 = StructWTupleGeneric { a: (1, 2) };

        let result = contract_methods
            .struct_w_generic_in_tuple(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // enum with generic in variant
        let arg1 = EnumWGeneric::B(10);
        let result = contract_methods
            .enum_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        contract_methods
            .unused_generic_args(StructUnusedGeneric::new(15), EnumUnusedGeneric::One(15))
            .call()
            .await?;

        let (the_struct, the_enum) = contract_methods
            .used_and_unused_generic_args(
                StructUsedAndUnusedGenericParams::new(10u8),
                EnumUsedAndUnusedGenericParams::Two(11u8),
            )
            .call()
            .await?
            .value;

        assert_eq!(the_struct.field, 12u8);
        if let EnumUsedAndUnusedGenericParams::Two(val) = the_enum {
            assert_eq!(val, 13)
        } else {
            panic!("Expected the variant EnumUsedAndUnusedGenericParams::Two");
        }
    }
    {
        // complex case
        let pass_through = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "ab".try_into()?,
            },
        };
        let w_arr_generic = StructWArrayGeneric {
            a: [pass_through.clone(), pass_through],
        };

        let arg1 = MegaExample {
            a: ([Bits256([0; 32]), Bits256([0; 32])], "ab".try_into()?),
            b: vec![(
                [EnumWGeneric::B(StructWTupleGeneric {
                    a: (w_arr_generic.clone(), w_arr_generic),
                })],
                10u32,
            )],
        };
        contract_methods.complex_test(arg1.clone()).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_vectors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/vectors"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let methods = contract_instance.methods();

    {
        // vec of u32s
        let arg = vec![0, 1, 2];
        methods.u32_vec(arg).call().await?;
    }
    {
        // vec of vecs of u32s
        let arg = vec![vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_vec(arg.clone()).call().await?;
    }
    {
        // vec of structs
        // ANCHOR: passing_in_vec
        let arg = vec![SomeStruct { a: 0 }, SomeStruct { a: 1 }];
        methods.struct_in_vec(arg.clone()).call().await?;
        // ANCHOR_END: passing_in_vec
    }
    {
        // vec in struct
        let arg = SomeStruct { a: vec![0, 1, 2] };
        methods.vec_in_struct(arg.clone()).call().await?;
    }
    {
        // array in vec
        let arg = vec![[0u64, 1u64], [0u64, 1u64]];
        methods.array_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in array
        let arg = [vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_array(arg.clone()).call().await?;
    }
    {
        // vec in enum
        let arg = SomeEnum::a(vec![0, 1, 2]);
        methods.vec_in_enum(arg.clone()).call().await?;
    }
    {
        // enum in vec
        let arg = vec![SomeEnum::a(0), SomeEnum::a(1)];
        methods.enum_in_vec(arg.clone()).call().await?;
    }
    {
        // tuple in vec
        let arg = vec![(0, 0), (1, 1)];
        methods.tuple_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in tuple
        let arg = (vec![0, 1, 2], vec![0, 1, 2]);
        methods.vec_in_tuple(arg.clone()).call().await?;
    }
    {
        // vec in a vec in a struct in a vec
        let arg = vec![
            SomeStruct {
                a: vec![vec![0, 1, 2], vec![3, 4, 5]],
            },
            SomeStruct {
                a: vec![vec![6, 7, 8], vec![9, 10, 11]],
            },
        ];
        methods
            .vec_in_a_vec_in_a_struct_in_a_vec(arg.clone())
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_b256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        Bits256([2; 32]),
        contract_instance
            .methods()
            .b256_as_output()
            .call()
            .await?
            .value
    );

    {
        // ANCHOR: 256_arg
        let b256 = Bits256([1; 32]);

        let call_handler = contract_instance.methods().b256_as_input(b256);
        // ANCHOR_END: 256_arg

        assert!(call_handler.call().await?.value);
    }

    Ok(())
}

#[tokio::test]
async fn test_b512() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b512"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: b512_example
    let hi_bits = Bits256::from_hex_str(
        "0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c",
    )?;
    let lo_bits = Bits256::from_hex_str(
        "0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
    )?;
    let b512 = B512::from((hi_bits, lo_bits));
    // ANCHOR_END: b512_example

    assert_eq!(b512, contract_methods.b512_as_output().call().await?.value);

    {
        let lo_bits2 = Bits256::from_hex_str(
            "0x54ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
        )?;
        let b512 = B512::from((hi_bits, lo_bits2));

        assert!(contract_methods.b512_as_input(b512).call().await?.value);
    }

    Ok(())
}

fn u128_from(parts: (u64, u64)) -> u128 {
    let bytes: [u8; 16] = [parts.0.to_be_bytes(), parts.1.to_be_bytes()]
        .concat()
        .try_into()
        .unwrap();
    u128::from_be_bytes(bytes)
}

#[tokio::test]
async fn test_u128() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u128"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u128_from((1, 2));

        let actual = contract_methods.u128_sum_and_ret(arg).call().await?.value;

        let expected = arg + u128_from((3, 4));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u128_in_enum_output().call().await?.value;

        let expected = SomeEnum::B(u128_from((4, 4)));
        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u128_from((3, 3)));

        contract_methods.u128_in_enum_input(input).call().await?;
    }

    Ok(())
}

fn u256_from(parts: (u64, u64, u64, u64)) -> U256 {
    let bytes: [u8; 32] = [
        parts.0.to_be_bytes(),
        parts.1.to_be_bytes(),
        parts.2.to_be_bytes(),
        parts.3.to_be_bytes(),
    ]
    .concat()
    .try_into()
    .unwrap();
    U256::from(bytes)
}

#[tokio::test]
async fn test_u256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u256_from((1, 2, 3, 4));
        let actual = contract_methods.u256_sum_and_ret(arg).call().await?.value;
        let expected = arg + u256_from((3, 4, 5, 6));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u256_in_enum_output().call().await?.value;
        let expected = SomeEnum::B(u256_from((1, 2, 3, 4)));

        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u256_from((2, 3, 4, 5)));

        contract_methods.u256_in_enum_input(input).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_base_type_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: returning_vec
    let response = contract_methods.u8_in_vec(10).call().await?;
    assert_eq!(response.value, (0..10).collect::<Vec<_>>());
    // ANCHOR_END: returning_vec

    let response = contract_methods.u16_in_vec(11).call().await?;
    assert_eq!(response.value, (0..11).collect::<Vec<_>>());

    let response = contract_methods.u32_in_vec(12).call().await?;
    assert_eq!(response.value, (0..12).collect::<Vec<_>>());

    let response = contract_methods.u64_in_vec(13).call().await?;
    assert_eq!(response.value, (0..13).collect::<Vec<_>>());

    let response = contract_methods.bool_in_vec().call().await?;
    assert_eq!(response.value, [true, false, true, false].to_vec());

    let response = contract_methods.b256_in_vec(13).call().await?;
    assert_eq!(response.value, vec![Bits256([2; 32]); 13]);

    Ok(())
}

#[tokio::test]
async fn test_composite_types_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let expected: Vec<[u64; 4]> = vec![[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
        let response = contract_methods.array_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    {
        let expected: Vec<Pasta> = vec![
            Pasta::Tortelini(Bimbam {
                bim: 1111,
                bam: 2222_u32,
            }),
            Pasta::Rigatoni(1987),
            Pasta::Spaghetti(true),
        ];

        let response = contract_methods.enum_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<Bimbam> = vec![
            Bimbam {
                bim: 1111,
                bam: 2222_u32,
            },
            Bimbam {
                bim: 3333,
                bam: 4444_u32,
            },
            Bimbam {
                bim: 5555,
                bam: 6666_u32,
            },
        ];
        let response = contract_methods.struct_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<(u64, u32)> = vec![(1111, 2222_u32), (3333, 4444_u32), (5555, 6666_u32)];
        let response = contract_methods.tuple_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<SizedAsciiString<4>> =
            vec!["hell".try_into()?, "ello".try_into()?, "lloh".try_into()?];
        let response = contract_methods.str_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    Ok(())
}

#[tokio::test]
async fn test_bytes_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesOutputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.return_bytes(10).call().await?;

    assert_eq!(response.value, (0..10).collect::<Vec<_>>());

    Ok(())
}

#[tokio::test]
async fn test_bytes_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesInputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesInputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: bytes_arg
        let bytes = Bytes(vec![40, 41, 42]);

        contract_methods.accept_bytes(bytes).call().await?;
        // ANCHOR_END: bytes_arg
    }
    {
        let bytes = Bytes(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![bytes.clone(), bytes.clone()],
            inner_enum: SomeEnum::Second(bytes),
        };

        contract_methods.accept_nested_bytes(wrapper).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_raw_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RawSliceContract",
            project = "e2e/sway/types/contracts/raw_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RawSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    {
        for length in 0u8..=10 {
            let response = contract_methods.return_raw_slice(length).call().await?;
            assert_eq!(response.value, (0u8..length).collect::<Vec<u8>>());
        }
    }
    {
        contract_methods
            .accept_raw_slice(RawSlice(vec![40, 41, 42]))
            .call()
            .await?;
    }
    {
        let raw_slice = RawSlice(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![raw_slice.clone(), raw_slice.clone()],
            inner_enum: SomeEnum::Second(raw_slice),
        };

        contract_methods
            .accept_nested_raw_slice(wrapper)
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_string_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StringSliceContract",
            project = "e2e/sway/types/contracts/string_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StringSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .handles_str("contract-input".try_into()?)
        .call()
        .await?;
    assert_eq!(response.value, "contract-return");

    Ok(())
}

#[tokio::test]
async fn contract_std_lib_string() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StdLibString",
            project = "e2e/sway/types/contracts/std_lib_string"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StdLibString",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.return_dynamic_string().call().await?.value;
        assert_eq!(resp, "Hello World");
    }
    {
        let _resp = contract_methods
            .accepts_dynamic_string(String::from("Hello World"))
            .call()
            .await?;
    }
    {
        // confirm encoding/decoding a string wasn't faulty and led to too high gas consumption
        let _resp = contract_methods
            .echoes_dynamic_string(String::from("Hello Fuel"))
            .with_tx_policies(TxPolicies::default().with_script_gas_limit(3600))
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_heap_type_in_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_type_in_enums"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.returns_bytes_result(true).call().await?;
        let expected = Ok(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_result(false).call().await?;
        let expected = Err(TestError::Something([255u8, 255u8, 255u8, 255u8, 255u8]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(true).call().await?;
        let expected = Ok(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(false).call().await?;
        let expected = Err(TestError::Else(7777));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(true).call().await?;
        let expected = Ok("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_str_result(true).call().await?;
        let expected = Ok("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(true).call().await?;
        let expected = Some(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_vec_option(true).call().await?;
        let expected = Some(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_string_option(true).call().await?;
        let expected = Some("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_str_option(true).call().await?;
        let expected = Some("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }

    Ok(())
}

#[tokio::test]
async fn nested_heap_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let arr = [2u8, 4, 8];
    let struct_generics = StructGenerics {
        one: Bytes(arr.to_vec()),
        two: String::from("fuel"),
        three: RawSlice(arr.to_vec()),
    };

    let enum_vec = [struct_generics.clone(), struct_generics].to_vec();
    let expected = EnumGeneric::One(enum_vec);

    let result = contract_instance
        .methods()
        .nested_heap_types()
        .call()
        .await?;

    assert_eq!(result.value, expected);

    Ok(())
}\n```

Note: you can still interact with contracts containing methods that return vectors nested inside another type, just not interact with the methods themselves

Converting Types

Below you can find examples for common type conversions:

Convert Between Native Types

You might want to convert between the native types (Bytes32, Address, ContractId, and AssetId). Because these types are wrappers on [u8; 32], converting is a matter of dereferencing one and instantiating the other using the dereferenced value. Here's an example:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert to Bytes32

Convert a [u8; 32] array to Bytes32:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert a hex string to Bytes32:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert to Address

Convert a [u8; 32] array to an Address:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert a Bech32 address to an Address:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert a wallet to an Address:

```rust\n#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[tokio::test]
    async fn create_random_wallet() -> Result<()> {
        // ANCHOR: create_random_wallet
        use fuels::prelude::*;

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_random(Some(provider));
        // ANCHOR_END: create_random_wallet

        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_secret_key() -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        // ANCHOR: create_wallet_from_secret_key
        use std::str::FromStr;

        use fuels::{crypto::SecretKey, prelude::*};

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Setup the private key.
        let secret = SecretKey::from_str(
            "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
        )?;

        // Create the wallet.
        let _wallet = WalletUnlocked::new_from_private_key(secret, Some(provider));
        // ANCHOR_END: create_wallet_from_secret_key
        Ok(())
    }

    #[tokio::test]
    async fn create_wallet_from_mnemonic() -> Result<()> {
        // ANCHOR: create_wallet_from_mnemonic
        use fuels::prelude::*;

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let _wallet = WalletUnlocked::new_from_mnemonic_phrase_with_path(
            phrase,
            Some(provider.clone()),
            "m/44'/1179993420'/0'/0/0",
        )?;

        // Or with the default derivation path
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let expected_address = "fuel17x9kg3k7hqf42396vqenukm4yf59e5k0vj4yunr4mae9zjv9pdjszy098t";

        assert_eq!(wallet.address().to_string(), expected_address);
        // ANCHOR_END: create_wallet_from_mnemonic
        Ok(())
    }

    #[tokio::test]
    async fn create_and_restore_json_wallet() -> Result<()> {
        // ANCHOR: create_and_restore_json_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();
        let mut rng = rand::thread_rng();

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        let password = "my_master_password";

        // Create a wallet to be stored in the keystore.
        let (_wallet, uuid) =
            WalletUnlocked::new_from_keystore(&dir, &mut rng, password, Some(provider.clone()))?;

        let path = dir.join(uuid);

        let _recovered_wallet = WalletUnlocked::load_keystore(path, password, Some(provider))?;
        // ANCHOR_END: create_and_restore_json_wallet
        Ok(())
    }

    #[tokio::test]
    async fn create_and_store_mnemonic_wallet() -> Result<()> {
        // ANCHOR: create_and_store_mnemonic_wallet
        use fuels::prelude::*;

        let dir = std::env::temp_dir();

        let phrase =
            "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

        // Use the test helper to setup a test provider.
        let provider = setup_test_provider(vec![], vec![], None, None).await?;

        // Create first account from mnemonic phrase.
        let wallet = WalletUnlocked::new_from_mnemonic_phrase(phrase, Some(provider))?;

        let password = "my_master_password";

        // Encrypts and stores it on disk. Can be recovered using `Wallet::load_keystore`.
        let _uuid = wallet.encrypt(&dir, password)?;
        // ANCHOR_END: create_and_store_mnemonic_wallet
        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer() -> Result<()> {
        // ANCHOR: wallet_transfer
        use fuels::prelude::*;

        // Setup 2 test wallets with 1 coin each
        let num_wallets = 2;
        let coins_per_wallet = 1;
        let coin_amount = 2;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount)),
            None,
            None,
        )
        .await?;

        // Transfer the base asset with amount 1 from wallet 1 to wallet 2
        let transfer_amount = 1;
        let asset_id = Default::default();
        let (_tx_id, _receipts) = wallets[0]
            .transfer(
                wallets[1].address(),
                transfer_amount,
                asset_id,
                TxPolicies::default(),
            )
            .await?;

        let wallet_2_final_coins = wallets[1].get_coins(AssetId::zeroed()).await?;

        // Check that wallet 2 now has 2 coins
        assert_eq!(wallet_2_final_coins.len(), 2);

        // ANCHOR_END: wallet_transfer
        Ok(())
    }

    #[tokio::test]
    async fn wallet_contract_transfer() -> Result<()> {
        use fuels::prelude::*;
        use rand::Fill;

        let mut rng = rand::thread_rng();

        let base_asset = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 1,
            coin_amount: 1000,
        };

        let mut random_asset_id = AssetId::zeroed();
        random_asset_id.try_fill(&mut rng).unwrap();
        let random_asset = AssetConfig {
            id: random_asset_id,
            num_coins: 3,
            coin_amount: 100,
        };

        let wallet_config = WalletsConfig::new_multiple_assets(1, vec![random_asset, base_asset]);
        let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None)
            .await?
            .pop()
            .unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: wallet_contract_transfer
        // Check the current balance of the contract with id 'contract_id'
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert!(contract_balances.is_empty());

        // Transfer an amount of 300 to the contract
        let amount = 300;
        let asset_id = random_asset_id;
        let (_tx_id, _receipts) = wallet
            .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default())
            .await?;

        // Check that the contract now has 1 coin
        let contract_balances = wallet
            .try_provider()?
            .get_contract_balances(&contract_id)
            .await?;
        assert_eq!(contract_balances.len(), 1);

        let random_asset_balance = contract_balances.get(&random_asset_id).unwrap();
        assert_eq!(*random_asset_balance, 300);
        // ANCHOR_END: wallet_contract_transfer

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_multiple_wallets() -> Result<()> {
        // ANCHOR: multiple_wallets_helper
        use fuels::prelude::*;
        // This helper will launch a local node and provide 10 test wallets linked to it.
        // The initial balance defaults to 1 coin per wallet with an amount of 1_000_000_000
        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
        // ANCHOR_END: multiple_wallets_helper
        // ANCHOR: setup_5_wallets
        let num_wallets = 5;
        let coins_per_wallet = 3;
        let amount_per_coin = 100;

        let config = WalletsConfig::new(
            Some(num_wallets),
            Some(coins_per_wallet),
            Some(amount_per_coin),
        );
        // Launches a local node and provides test wallets as specified by the config
        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        // ANCHOR_END: setup_5_wallets
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_multiple_assets() -> Result<()> {
        // ANCHOR: multiple_assets_wallet
        // ANCHOR: multiple_assets_coins
        use fuels::prelude::*;
        let mut wallet = WalletUnlocked::new_random(None);
        let num_assets = 5; // 5 different assets
        let coins_per_asset = 10; // Per asset id, 10 coins in the wallet
        let amount_per_coin = 15; // For each coin (UTXO) of the asset, amount of 15

        let (coins, asset_ids) = setup_multiple_assets_coins(
            wallet.address(),
            num_assets,
            coins_per_asset,
            amount_per_coin,
        );
        // ANCHOR_END: multiple_assets_coins
        let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: multiple_assets_wallet
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn setup_wallet_custom_assets() -> std::result::Result<(), Box<dyn std::error::Error>> {
        // ANCHOR: custom_assets_wallet
        use fuels::prelude::*;
        use rand::Fill;

        let mut wallet = WalletUnlocked::new_random(None);
        let mut rng = rand::thread_rng();

        let asset_base = AssetConfig {
            id: AssetId::zeroed(),
            num_coins: 2,
            coin_amount: 4,
        };

        let mut asset_id_1 = AssetId::zeroed();
        asset_id_1.try_fill(&mut rng)?;
        let asset_1 = AssetConfig {
            id: asset_id_1,
            num_coins: 6,
            coin_amount: 8,
        };

        let mut asset_id_2 = AssetId::zeroed();
        asset_id_2.try_fill(&mut rng)?;
        let asset_2 = AssetConfig {
            id: asset_id_2,
            num_coins: 10,
            coin_amount: 12,
        };

        let assets = vec![asset_base, asset_1, asset_2];

        let coins = setup_custom_assets_coins(wallet.address(), &assets);
        let provider = setup_test_provider(coins, vec![], None, None).await?;
        wallet.set_provider(provider);
        // ANCHOR_END: custom_assets_wallet
        // ANCHOR: custom_assets_wallet_short
        let num_wallets = 1;
        let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        // ANCHOR_END: custom_assets_wallet_short

        // ANCHOR: wallet_to_address
        let wallet_unlocked = WalletUnlocked::new_random(None);
        let address: Address = wallet_unlocked.address().into();
        // ANCHOR_END: wallet_to_address
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_balances() -> Result<()> {
        use std::collections::HashMap;

        use fuels::{
            prelude::{launch_provider_and_get_wallet, DEFAULT_COIN_AMOUNT, DEFAULT_NUM_COINS},
            types::AssetId,
        };

        let wallet = launch_provider_and_get_wallet().await?;
        // ANCHOR: get_asset_balance
        let asset_id = AssetId::zeroed();
        let balance: u64 = wallet.get_asset_balance(&asset_id).await?;
        // ANCHOR_END: get_asset_balance
        // ANCHOR: get_balances
        let balances: HashMap<String, u128> = wallet.get_balances().await?;
        // ANCHOR_END: get_balances

        // ANCHOR: get_balance_hashmap
        let asset_balance = balances.get(&asset_id.to_string()).unwrap();
        // ANCHOR_END: get_balance_hashmap

        assert_eq!(
            *asset_balance,
            (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128
        );

        Ok(())
    }

    #[tokio::test]
    async fn wallet_transfer_to_base_layer() -> Result<()> {
        // ANCHOR: wallet_withdraw_to_base
        use std::str::FromStr;

        use fuels::prelude::*;

        let wallets = launch_custom_provider_and_get_wallets(
            WalletsConfig::new(Some(1), None, None),
            None,
            None,
        )
        .await?;
        let wallet = wallets.first().unwrap();

        let amount = 1000;
        let base_layer_address = Address::from_str(
            "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
        )?;
        let base_layer_address = Bech32Address::from(base_layer_address);
        // Transfer an amount of 1000 to the specified base layer address
        let (tx_id, msg_id, _receipts) = wallet
            .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default())
            .await?;

        let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?;

        // Retrieve a message proof from the provider
        let proof = wallet
            .try_provider()?
            .get_message_proof(&tx_id, &msg_id, None, Some(2))
            .await?;

        // Verify the amount and recipient
        assert_eq!(proof.amount, amount);
        assert_eq!(proof.recipient, base_layer_address);
        // ANCHOR_END: wallet_withdraw_to_base

        Ok(())
    }
}\n```

Convert a hex string to an Address:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert to ContractId

Convert a [u8; 32] array to ContractId:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert a hex string to a ContractId:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert a contract instance to a ContractId:

```rust\nuse fuels::{
    core::codec::DecoderConfig,
    prelude::*,
    types::{errors::transaction::Reason, AsciiString, Bits256, SizedAsciiString},
};

#[tokio::test]
async fn test_parse_logged_variables() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: produce_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_variables().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_bits256 = response.decode_logs_with_type::<Bits256>()?;
    let log_string = response.decode_logs_with_type::<SizedAsciiString<4>>()?;
    let log_array = response.decode_logs_with_type::<[u8; 3]>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_bits256, vec![expected_bits256]);
    assert_eq!(log_string, vec!["Fuel"]);
    assert_eq!(log_array, vec![[1, 2, 3]]);
    // ANCHOR_END: produce_logs

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_values() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_values().call().await?;

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_custom_types().call().await?;

    let log_test_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_test_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_tuple = response.decode_logs_with_type::<(TestStruct, TestEnum)>()?;

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;

    assert_eq!(log_test_struct, vec![expected_struct.clone()]);
    assert_eq!(log_test_enum, vec![expected_enum.clone()]);
    assert_eq!(log_tuple, vec![(expected_struct, expected_enum)]);

    Ok(())
}

#[tokio::test]
async fn test_parse_logs_generic_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_logs_generic_types().call().await?;

    let log_struct = response.decode_logs_with_type::<StructWithGeneric<[_; 3]>>()?;
    let log_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_struct_nested =
        response.decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<[_; 3]>>>()?;
    let log_struct_deeply_nested = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<[_; 3]>>,
    >>()?;

    let l = [1u8, 2u8, 3u8];
    let expected_struct = StructWithGeneric {
        field_1: l,
        field_2: 64,
    };
    let expected_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_struct_nested, vec![expected_nested_struct]);
    assert_eq!(
        log_struct_deeply_nested,
        vec![expected_deeply_nested_struct]
    );

    Ok(())
}

#[tokio::test]
async fn test_decode_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // ANCHOR: decode_logs
    let contract_methods = contract_instance.methods();
    let response = contract_methods.produce_multiple_logs().call().await?;
    let logs = response.decode_logs();
    // ANCHOR_END: decode_logs

    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_generic_struct:?}"),
    ];

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
async fn test_decode_logs_with_no_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let logs = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .decode_logs();

    assert!(logs.filter_succeeded().is_empty());

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.produce_logs_values();
    let call_handler_2 = contract_methods.produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_log_multiple_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/logs/contract_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_handler_1 = contract_instance.methods().produce_logs_values();
    let call_handler_2 = contract_instance2.methods().produce_logs_variables();

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!(
            "{:?}",
            Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ])
        ),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs"),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id.clone())
        .with_contracts(&[&contract_instance]);

    let call_handler_2 = contract_caller_instance2
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = multi_call_handler.call::<((), ())>().await?.decode_logs();

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

fn assert_revert_containing_msg(msg: &str, error: Error) {
    assert!(matches!(error, Error::Transaction(Reason::Reverted { .. })));
    if let Error::Transaction(Reason::Reverted { reason, .. }) = error {
        assert!(
            reason.contains(msg),
            "message: \"{msg}\" not contained in reason: \"{reason}\""
        );
    }
}

#[tokio::test]
async fn test_revert_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        ($method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .call()
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
        ($method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method()
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");

            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(require_primitive, call, "42");
        reverts_with_msg!(require_primitive, simulate, "42");

        reverts_with_msg!(require_string, call, "fuel");
        reverts_with_msg!(require_string, simulate, "fuel");

        reverts_with_msg!(require_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            require_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(require_with_additional_logs, call, "64");
        reverts_with_msg!(require_with_additional_logs, simulate, "64");
    }
    {
        reverts_with_msg!(rev_w_log_primitive, call, "42");
        reverts_with_msg!(rev_w_log_primitive, simulate, "42");

        reverts_with_msg!(rev_w_log_string, call, "fuel");
        reverts_with_msg!(rev_w_log_string, simulate, "fuel");

        reverts_with_msg!(rev_w_log_custom_generic, call, "StructDeeplyNestedGeneric");
        reverts_with_msg!(
            rev_w_log_custom_generic,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_single_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_multi_call_revert_logs_multi_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertLogsContract",
            project = "e2e/sway/contracts/revert_logs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "RevertLogsContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let contract_methods2 = contract_instance2.methods();

    // The output of the error depends on the order of the contract
    // handlers as the script returns the first revert it finds.
    {
        let call_handler_1 = contract_methods.require_string();
        let call_handler_2 = contract_methods2.rev_w_log_custom_generic();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("fuel", error);
    }
    {
        let call_handler_1 = contract_methods2.require_custom_generic();
        let call_handler_2 = contract_methods.rev_w_log_string();

        let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let error = multi_call_handler
            .simulate::<((), ())>(Execution::Realistic)
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);

        let error = multi_call_handler
            .call::<((), ())>()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("StructDeeplyNestedGeneric", error);
    }

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_decode_logs() -> Result<()> {
    // ANCHOR: script_logs
    abigen!(Script(
        name = "LogScript",
        abi = "e2e/sway/logs/script_logs/out/release/script_logs-abi.json"
    ));

    let wallet = launch_provider_and_get_wallet().await?;
    let bin_path = "sway/logs/script_logs/out/release/script_logs.bin";
    let instance = LogScript::new(wallet.clone(), bin_path);

    let response = instance.main().call().await?;

    let logs = response.decode_logs();
    let log_u64 = response.decode_logs_with_type::<u64>()?;
    // ANCHOR_END: script_logs

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_tuple = (expected_struct.clone(), expected_enum.clone());
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };
    let expected_logs: Vec<String> = vec![
        format!("{:?}", 128u64),
        format!("{:?}", 32u32),
        format!("{:?}", 16u16),
        format!("{:?}", 8u8),
        format!("{:?}", 64u64),
        format!("{expected_bits256:?}"),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
        format!("{expected_struct:?}"),
        format!("{expected_enum:?}"),
        format!("{expected_tuple:?}"),
        format!("{expected_generic_struct:?}"),
        format!("{expected_generic_enum:?}"),
        format!("{expected_nested_struct:?}"),
        format!("{expected_deeply_nested_struct:?}"),
    ];

    assert_eq!(logs.filter_succeeded(), expected_logs);

    Ok(())
}

#[tokio::test]
async fn test_contract_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/logs/contract_with_contract_logs",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/logs/contract_logs/out/release/contract_logs.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let expected_logs: Vec<String> = vec![
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
    ];

    let logs = contract_caller_instance
        .methods()
        .logs_from_external_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?
        .decode_logs();

    assert_eq!(expected_logs, logs.filter_succeeded());

    Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn test_script_logs_with_contract_logs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(name = "MyContract", project = "e2e/sway/logs/contract_logs",),
            Script(
                name = "LogScript",
                project = "e2e/sway/logs/script_with_contract_logs"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let expected_num_contract_logs = 4;

    let expected_script_logs: Vec<String> = vec![
        // Contract logs
        format!("{:?}", 64),
        format!("{:?}", 32),
        format!("{:?}", 16),
        format!("{:?}", 8),
        // Script logs
        format!("{:?}", true),
        format!("{:?}", 42),
        format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
        format!("{:?}", [1, 2, 3]),
    ];

    // ANCHOR: instance_to_contract_id
    let contract_id: ContractId = contract_instance.id().into();
    // ANCHOR_END: instance_to_contract_id

    // ANCHOR: external_contract_ids
    let response = script_instance
        .main(contract_id)
        .with_contract_ids(&[contract_id.into()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    // ANCHOR: external_contract
    let response = script_instance
        .main(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    {
        let num_contract_logs = response
            .receipts
            .iter()
            .filter(|receipt| matches!(receipt, Receipt::LogData { id, .. } | Receipt::Log { id, .. } if *id == contract_id))
            .count();

        assert_eq!(num_contract_logs, expected_num_contract_logs);
    }
    {
        let logs = response.decode_logs();

        assert_eq!(logs.filter_succeeded(), expected_script_logs);
    }

    Ok(())
}

#[tokio::test]
async fn test_script_decode_logs_with_type() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let response = script_instance.main().call().await?;

    let l = [1u8, 2u8, 3u8];
    let expected_bits256 = Bits256([
        239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60,
        239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
    ]);
    let expected_struct = TestStruct {
        field_1: true,
        field_2: expected_bits256,
        field_3: 64,
    };
    let expected_enum = TestEnum::VariantTwo;
    let expected_generic_struct = StructWithGeneric {
        field_1: expected_struct.clone(),
        field_2: 64,
    };

    let expected_generic_enum = EnumWithGeneric::VariantOne(l);
    let expected_nested_struct = StructWithNestedGeneric {
        field_1: expected_generic_struct.clone(),
        field_2: 64,
    };
    let expected_deeply_nested_struct = StructDeeplyNestedGeneric {
        field_1: expected_nested_struct.clone(),
        field_2: 64,
    };

    let log_u64 = response.decode_logs_with_type::<u64>()?;
    let log_u32 = response.decode_logs_with_type::<u32>()?;
    let log_u16 = response.decode_logs_with_type::<u16>()?;
    let log_u8 = response.decode_logs_with_type::<u8>()?;
    let log_struct = response.decode_logs_with_type::<TestStruct>()?;
    let log_enum = response.decode_logs_with_type::<TestEnum>()?;
    let log_generic_struct = response.decode_logs_with_type::<StructWithGeneric<TestStruct>>()?;
    let log_generic_enum = response.decode_logs_with_type::<EnumWithGeneric<[_; 3]>>()?;
    let log_nested_struct = response
        .decode_logs_with_type::<StructWithNestedGeneric<StructWithGeneric<TestStruct>>>()?;
    let log_deeply_nested_struct = response.decode_logs_with_type::<StructDeeplyNestedGeneric<
        StructWithNestedGeneric<StructWithGeneric<TestStruct>>,
    >>()?;
    // try to retrieve non existent log
    let log_nonexistent = response.decode_logs_with_type::<bool>()?;

    assert_eq!(log_u64, vec![128, 64]);
    assert_eq!(log_u32, vec![32]);
    assert_eq!(log_u16, vec![16]);
    assert_eq!(log_u8, vec![8]);
    assert_eq!(log_struct, vec![expected_struct]);
    assert_eq!(log_enum, vec![expected_enum]);
    assert_eq!(log_generic_struct, vec![expected_generic_struct]);
    assert_eq!(log_generic_enum, vec![expected_generic_enum]);
    assert_eq!(log_nested_struct, vec![expected_nested_struct]);
    assert_eq!(
        log_deeply_nested_struct,
        vec![expected_deeply_nested_struct]
    );
    assert!(log_nonexistent.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_script_require_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_revert_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    {
        reverts_with_msg!(MatchEnum::RequirePrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RequirePrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RequireString, call, "fuel");
        reverts_with_msg!(MatchEnum::RequireString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RequireCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );

        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, call, "64");
        reverts_with_msg!(MatchEnum::RequireWithAdditionalLogs, simulate, "64");
    }
    {
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, call, "42");
        reverts_with_msg!(MatchEnum::RevWLogPrimitive, simulate, "42");

        reverts_with_msg!(MatchEnum::RevWLogString, call, "fuel");
        reverts_with_msg!(MatchEnum::RevWLogString, simulate, "fuel");

        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            call,
            "StructDeeplyNestedGeneric"
        );
        reverts_with_msg!(
            MatchEnum::RevWLogCustomGeneric,
            simulate,
            "StructDeeplyNestedGeneric"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let error = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_contract_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Contract(
                name = "ContractLogs",
                project = "e2e/sway/logs/contract_logs",
            ),
            Contract(
                name = "ContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller",
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "ContractLogs",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "ContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = Contract::load_from(
        "./sway/contracts/lib_contract/out/release/lib_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let lib_contract_instance = MyContract::new(contract_id.clone(), wallet.clone());

    let call_handler_1 = contract_instance.methods().produce_logs_values();

    let call_handler_2 = contract_caller_instance
        .methods()
        .require_from_contract(contract_id)
        .with_contracts(&[&lib_contract_instance]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let error = multi_call_handler
        .call::<((), ())>()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_script_require_from_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet",
            random_salt = false,
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

#[tokio::test]
async fn test_loader_script_require_from_loader_contract() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/lib_contract",
            ),
            Script(
                name = "LogScript",
                project = "e2e/sway/scripts/require_from_contract"
            )
        ),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    let contract_binary = "sway/contracts/lib_contract/out/release/lib_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;
    let contract_instance = MyContract::new(contract_id, wallet);

    let mut script_instance = script_instance;
    script_instance.convert_into_loader().await?;

    let error = script_instance
        .main(contract_instance.id())
        .with_contracts(&[&contract_instance])
        .call()
        .await
        .expect_err("should return a revert error");

    assert_revert_containing_msg("require from contract", error);

    Ok(())
}

fn assert_assert_eq_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left == right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

fn assert_assert_ne_containing_msg<T: std::fmt::Debug>(left: T, right: T, error: Error) {
    let msg = format!(
        "assertion failed: `(left != right)`\n left: `\"{left:?}\"`\n right: `\"{right:?}\"`"
    );
    assert_revert_containing_msg(&msg, error)
}

#[tokio::test]
async fn test_contract_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LogContract",
            project = "e2e/sway/contracts/asserts"
        )),
        Deploy(
            name = "contract_instance",
            contract = "LogContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    macro_rules! reverts_with_msg {
        (($($arg: expr,)*), $method:ident, call, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        (($($arg: expr,)*), $method:ident, simulate, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!((32, 64,), assert_primitive, call, "assertion failed");
        reverts_with_msg!((32, 64,), assert_primitive, simulate, "assertion failed");
    }

    macro_rules! reverts_with_assert_eq_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_eq_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, call, "assertion failed");
        reverts_with_assert_eq_msg!((32, 64,), assert_eq_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_struct.clone(), test_struct2.clone(),),
            assert_eq_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;
        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_eq_msg!(
            (test_enum.clone(), test_enum2.clone(),),
            assert_eq_enum,
            simulate,
            "assertion failed"
        );
    }

    macro_rules! reverts_with_assert_ne_msg {
        (($($arg: expr,)*), $method:ident, $execution: ident, $msg:expr) => {
            let error = contract_instance
                .methods()
                .$method($($arg,)*)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_assert_ne_containing_msg($($arg,)* error);
        }
    }

    {
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, call, "assertion failed");
        reverts_with_assert_ne_msg!((32, 32,), assert_ne_primitive, simulate, "assertion failed");
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_struct.clone(), test_struct.clone(),),
            assert_ne_struct,
            simulate,
            "assertion failed"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            call,
            "assertion failed"
        );

        reverts_with_assert_ne_msg!(
            (test_enum.clone(), test_enum.clone(),),
            assert_ne_enum,
            simulate,
            "assertion failed"
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_script_asserts_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/scripts/script_asserts"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    macro_rules! reverts_with_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }

    macro_rules! reverts_with_assert_eq_ne_msg {
        ($arg:expr, call, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .call()
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
        ($arg:expr, simulate, $msg:expr) => {
            let error = script_instance
                .main($arg)
                .simulate(Execution::Realistic)
                .await
                .expect_err("should return a revert error");
            assert_revert_containing_msg($msg, error);
        };
    }
    {
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            call,
            "assertion failed"
        );
        reverts_with_msg!(
            MatchEnum::AssertPrimitive((32, 64)),
            simulate,
            "assertion failed"
        );
    }
    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqPrimitive((32, 64)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };

        let test_struct2 = TestStruct {
            field_1: false,
            field_2: 32,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqStruct((test_struct.clone(), test_struct2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;
        let test_enum2 = TestEnum::VariantTwo;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            call,
            "assertion failed: `(left == right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertEqEnum((test_enum.clone(), test_enum2.clone(),)),
            simulate,
            "assertion failed: `(left == right)`"
        );
    }

    {
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNePrimitive((32, 32)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_struct = TestStruct {
            field_1: true,
            field_2: 64,
        };
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeStruct((test_struct.clone(), test_struct.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }
    {
        let test_enum = TestEnum::VariantOne;

        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            call,
            "assertion failed: `(left != right)`"
        );
        reverts_with_assert_eq_ne_msg!(
            MatchEnum::AssertNeEnum((test_enum.clone(), test_enum.clone(),)),
            simulate,
            "assertion failed: `(left != right)`"
        );
    }

    Ok(())
}

#[tokio::test]
async fn contract_token_ops_error_messages() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/token_ops"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let contract_id = contract_instance.contract_id();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());
        let address = wallet.address();

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .simulate(Execution::Realistic)
            .await
            .expect_err("should return a revert error");
        assert_revert_containing_msg("failed transfer to address", error);

        let error = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .call()
            .await
            .expect_err("should return a revert error");

        assert_revert_containing_msg("failed transfer to address", error);
    }

    Ok(())
}

#[tokio::test]
async fn test_log_results() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let response = contract_instance
        .methods()
        .produce_bad_logs()
        .call()
        .await?;

    let log = response.decode_logs();

    let expected_err = format!(
        "codec: missing log formatter for log_id: `LogId({:?}, \"128\")`, data: `{:?}`. \
         Consider adding external contracts using `with_contracts()`",
        contract_instance.id().hash,
        [0u8; 8]
    );

    let succeeded = log.filter_succeeded();
    let failed = log.filter_failed();
    assert_eq!(succeeded, vec!["123".to_string()]);
    assert_eq!(failed.first().unwrap().to_string(), expected_err);

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_contract_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: decoding with too low max_tokens fails
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "Should have failed since there are more tokens than what is supported by default.",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "Should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Single call: increasing limits makes the test pass
        let response = methods
            .i_log_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }
    {
        // Multi call: decoding with too low max_tokens will fail
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        response.decode_logs_with_type::<[u8; 1000]>().expect_err(
            "should have failed since there are more tokens than what is supported by default",
        );

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty(), "should have had failed to decode logs since there are more tokens than what is supported by default");
    }
    {
        // Multi call: increasing limits makes the test pass
        let response = CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_log_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<((),)>()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty());
    }

    Ok(())
}

#[tokio::test]
async fn can_configure_decoder_for_script_log_decoding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_needs_custom_decoder_logging"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );

    {
        // Cannot decode the produced log with too low max_tokens
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 100,
                ..Default::default()
            })
            .call()
            .await?;

        response
            .decode_logs_with_type::<[u8; 1000]>()
            .expect_err("Cannot decode the log with default decoder config");

        let logs = response.decode_logs();
        assert!(!logs.filter_failed().is_empty())
    }
    {
        // When the token limit is bumped log decoding succeeds
        let response = script_instance
            .main()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?;

        let logs = response.decode_logs_with_type::<[u8; 1000]>()?;
        assert_eq!(logs, vec![[0u8; 1000]]);

        let logs = response.decode_logs();
        assert!(!logs.filter_succeeded().is_empty())
    }

    Ok(())
}

#[tokio::test]
async fn contract_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/logs/contract_logs"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.produce_string_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let response = contract_methods.produce_string_log().call().await?;
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let response = contract_methods.produce_bytes_log().call().await?;
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let response = contract_methods.produce_raw_slice_log().call().await?;
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let response = contract_methods.produce_vec_log().call().await?;
        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}

#[tokio::test]
async fn script_heap_log() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Script(
            name = "LogScript",
            project = "e2e/sway/logs/script_heap_logs"
        )),
        LoadScript(
            name = "script_instance",
            script = "LogScript",
            wallet = "wallet"
        )
    );
    let response = script_instance.main().call().await?;

    {
        let logs = response.decode_logs_with_type::<AsciiString>()?;

        assert_eq!("fuel".to_string(), logs.first().unwrap().to_string());
    }
    {
        let logs = response.decode_logs_with_type::<String>()?;

        assert_eq!(vec!["fuel".to_string()], logs);
    }
    {
        let logs = response.decode_logs_with_type::<Bytes>()?;

        assert_eq!(vec![Bytes("fuel".as_bytes().to_vec())], logs);
    }
    {
        let logs = response.decode_logs_with_type::<RawSlice>()?;

        assert_eq!(vec![RawSlice("fuel".as_bytes().to_vec())], logs);
    }
    {
        let v = [1u16, 2, 3].to_vec();
        let some_enum = EnumWithGeneric::VariantOne(v);
        let other_enum = EnumWithGeneric::VariantTwo;
        let v1 = vec![some_enum.clone(), other_enum, some_enum];
        let expected_vec = vec![vec![v1.clone(), v1]];

        let logs = response.decode_logs_with_type::<Vec<Vec<Vec<EnumWithGeneric<Vec<u16>>>>>>()?;

        assert_eq!(vec![expected_vec], logs);
    }

    Ok(())
}\n```

Convert to Identity

Convert an Address to an Identity:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert a ContractId to an Identity:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert to AssetId

Convert a [u8; 32] array to an AssetId:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert a hex string to an AssetId:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert to Bech32

Convert a [u8; 32] array to a Bech32 address:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert Bytes32 to a Bech32 address:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert a string to a Bech32 address:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert an Address to a Bech32 address:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert to str

Convert a ContractId to a str:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert an Address to a str:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert an AssetId to a str:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert Bytes32 to a str:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert to Bits256

Convert a hex string to Bits256:

```rust\nuse fuel_types::AssetId;
use fuels_macros::{Parameterize, Tokenizable, TryFrom};

use crate::types::errors::Result;

// A simple wrapper around [u8; 32] representing the `b256` type. Exists
// mainly so that we may differentiate `Parameterize` and `Tokenizable`
// implementations from what otherwise is just an array of 32 u8's.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct Bits256(pub [u8; 32]);

impl Bits256 {
    /// Returns `Self` with zeroes inside.
    pub fn zeroed() -> Self {
        Self([0; 32])
    }

    /// Create a new `Bits256` from a string representation of a hex.
    /// Accepts both `0x` prefixed and non-prefixed hex strings.
    pub fn from_hex_str(hex: &str) -> Result<Self> {
        let hex = if let Some(stripped_hex) = hex.strip_prefix("0x") {
            stripped_hex
        } else {
            hex
        };

        let mut bytes = [0u8; 32];
        hex::decode_to_slice(hex, &mut bytes as &mut [u8])?;

        Ok(Bits256(bytes))
    }
}

impl From<AssetId> for Bits256 {
    fn from(value: AssetId) -> Self {
        Self(value.into())
    }
}

// A simple wrapper around [Bits256; 2] representing the `B512` type.
#[derive(Debug, PartialEq, Eq, Copy, Clone, Parameterize, Tokenizable, TryFrom)]
#[FuelsCorePath = "crate"]
#[FuelsTypesPath = "crate::types"]
// ANCHOR: b512
pub struct B512 {
    pub bytes: [Bits256; 2],
}
// ANCHOR_END: b512

impl From<(Bits256, Bits256)> for B512 {
    fn from(bits_tuple: (Bits256, Bits256)) -> Self {
        B512 {
            bytes: [bits_tuple.0, bits_tuple.1],
        }
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone, Parameterize, Tokenizable, TryFrom)]
#[FuelsCorePath = "crate"]
#[FuelsTypesPath = "crate::types"]
// ANCHOR: evm_address
pub struct EvmAddress {
    // An evm address is only 20 bytes, the first 12 bytes should be set to 0
    value: Bits256,
}
// ANCHOR_END: evm_address
impl EvmAddress {
    fn new(b256: Bits256) -> Self {
        Self {
            value: Bits256(Self::clear_12_bytes(b256.0)),
        }
    }

    pub fn value(&self) -> Bits256 {
        self.value
    }

    // sets the leftmost 12 bytes to zero
    fn clear_12_bytes(bytes: [u8; 32]) -> [u8; 32] {
        let mut bytes = bytes;
        bytes[..12].copy_from_slice(&[0u8; 12]);

        bytes
    }
}

impl From<Bits256> for EvmAddress {
    fn from(b256: Bits256) -> Self {
        EvmAddress::new(b256)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        traits::{Parameterize, Tokenizable},
        types::{param_types::ParamType, Token},
    };

    #[test]
    fn from_hex_str_b256() -> Result<()> {
        // ANCHOR: from_hex_str
        let hex_str = "0101010101010101010101010101010101010101010101010101010101010101";

        let bits256 = Bits256::from_hex_str(hex_str)?;

        assert_eq!(bits256.0, [1u8; 32]);

        // With the `0x0` prefix
        // ANCHOR: hex_str_to_bits256
        let hex_str = "0x0101010101010101010101010101010101010101010101010101010101010101";

        let bits256 = Bits256::from_hex_str(hex_str)?;
        // ANCHOR_END: hex_str_to_bits256

        assert_eq!(bits256.0, [1u8; 32]);
        // ANCHOR_END: from_hex_str

        Ok(())
    }

    #[test]
    fn test_param_type_evm_addr() {
        assert_eq!(
            EvmAddress::param_type(),
            ParamType::Struct {
                name: "EvmAddress".to_string(),
                fields: vec![("value".to_string(), ParamType::B256)],
                generics: vec![]
            }
        );
    }

    #[test]
    fn evm_address_clears_first_12_bytes() -> Result<()> {
        let data = [1u8; 32];
        let address = EvmAddress::new(Bits256(data));

        let expected_data = Bits256([
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 1,
        ]);

        assert_eq!(address.value(), expected_data);

        Ok(())
    }

    #[test]
    fn test_into_token_evm_addr() {
        let bits = [1u8; 32];
        let evm_address = EvmAddress::from(Bits256(bits));

        let token = evm_address.into_token();

        let expected_data = [
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 1,
        ];

        assert_eq!(token, Token::Struct(vec![Token::B256(expected_data)]));
    }
}\n```

Convert a ContractId to Bits256:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert an Address to Bits256:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert an AssetId to Bits256:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Convert to Bytes

Convert a string to Bytes:

```rust\nuse crate::types::errors::Result;

#[derive(Debug, PartialEq, Clone, Eq)]
pub struct Bytes(pub Vec<u8>);

impl Bytes {
    /// Create a new `Bytes` from a string representation of a hex.
    /// Accepts both `0x` prefixed and non-prefixed hex strings.
    pub fn from_hex_str(hex: &str) -> Result<Self> {
        let hex = if let Some(stripped_hex) = hex.strip_prefix("0x") {
            stripped_hex
        } else {
            hex
        };
        let bytes = hex::decode(hex)?;

        Ok(Bytes(bytes))
    }
}

impl From<Bytes> for Vec<u8> {
    fn from(bytes: Bytes) -> Vec<u8> {
        bytes.0
    }
}

impl PartialEq<Vec<u8>> for Bytes {
    fn eq(&self, other: &Vec<u8>) -> bool {
        self.0 == *other
    }
}

impl PartialEq<Bytes> for Vec<u8> {
    fn eq(&self, other: &Bytes) -> bool {
        *self == other.0
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn from_hex_str_b256() -> Result<()> {
        // ANCHOR: bytes_from_hex_str
        let hex_str = "0101010101010101010101010101010101010101010101010101010101010101";

        let bytes = Bytes::from_hex_str(hex_str)?;

        assert_eq!(bytes.0, vec![1u8; 32]);

        // With the `0x0` prefix
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0101010101010101010101010101010101010101010101010101010101010101";

        let bytes = Bytes::from_hex_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32

        assert_eq!(bytes.0, vec![1u8; 32]);
        // ANCHOR_END: bytes_from_hex_str

        Ok(())
    }
}\n```

Convert to B512

Convert two hex strings to B512:

```rust\nuse std::str::FromStr;

use fuels::{
    prelude::*,
    types::{Bits256, EvmAddress, Identity, SizedAsciiString, B512, U256},
};

pub fn null_contract_id() -> Bech32ContractId {
    // a bech32 contract address that decodes to [0u8;32]
    Bech32ContractId::from_str("fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2")
        .expect("is valid")
}

#[tokio::test]
async fn test_methods_typeless_argument() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/empty_arguments"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .method_with_empty_argument()
        .call()
        .await?;

    assert_eq!(response.value, 63);

    Ok(())
}

#[tokio::test]
async fn call_with_empty_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/types/contracts/call_empty_return"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let _response = contract_instance.methods().store_value(42).call().await?;
    Ok(())
}

#[tokio::test]
async fn call_with_structs() -> Result<()> {
    // Generates the bindings from the an ABI definition inline.
    // The generated bindings can be accessed through `MyContract`.
    // ANCHOR: struct_generation
    abigen!(Contract(name="MyContract",
                     abi="e2e/sway/types/contracts/complex_types_contract/out/release/complex_types_contract-abi.json"));

    // Here we can use `CounterConfig`, a struct originally
    // defined in the contract.
    let counter_config = CounterConfig {
        dummy: true,
        initial_value: 42,
    };
    // ANCHOR_END: struct_generation

    let wallet = launch_provider_and_get_wallet().await?;

    let contract_id = Contract::load_from(
        "sway/types/contracts/complex_types_contract/out/release/complex_types_contract.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_methods = MyContract::new(contract_id, wallet).methods();

    let response = contract_methods
        .initialize_counter(counter_config)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let response = contract_methods.increment_counter(10).call().await?;

    assert_eq!(52, response.value);

    Ok(())
}

#[tokio::test]
async fn abigen_different_structs_same_arg_name() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/two_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let param_one = StructOne { foo: 42 };
    let param_two = StructTwo { bar: 42 };

    let contract_methods = contract_instance.methods();
    let res_one = contract_methods.something(param_one).call().await?;

    assert_eq!(res_one.value, 43);

    let res_two = contract_methods.something_else(param_two).call().await?;

    assert_eq!(res_two.value, 41);
    Ok(())
}

#[tokio::test]
async fn nested_structs() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/nested_structs"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = AllStruct {
        some_struct: SomeStruct {
            field: 12345,
            field_2: true,
        },
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_struct().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_struct_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the argument correctly. Investigate!"
    );

    let memory_address = MemoryAddress {
        contract_id: ContractId::zeroed(),
        function_selector: 10,
        function_data: 0,
    };

    let call_data = CallData {
        memory_address,
        num_coins_to_forward: 10,
        asset_id_of_coins_to_forward: ContractId::zeroed(),
        amount_of_gas_to_forward: 5,
    };

    let actual = contract_methods
        .nested_struct_with_reserved_keyword_substring(call_data.clone())
        .call()
        .await?
        .value;

    assert_eq!(actual, call_data);
    Ok(())
}

#[tokio::test]
async fn calls_with_empty_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/complex_types_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.get_empty_struct().call().await?;

        assert_eq!(response.value, EmptyStruct {});
    }
    {
        let response = contract_methods
            .input_empty_struct(EmptyStruct {})
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_struct_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let cocktail_in_bytes: Vec<u8> = vec![
        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3,
    ];

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(2),
        glass: 3,
    };

    // as slice
    let actual: Cocktail = cocktail_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Cocktail = (&cocktail_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Cocktail = cocktail_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn test_tuples() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/tuples"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let response = contract_methods.returns_tuple((1, 2)).call().await?;

        assert_eq!(response.value, (1, 2));
    }
    {
        // Tuple with struct.
        let my_struct_tuple = (
            42,
            Person {
                name: "Jane".try_into()?,
            },
        );
        let response = contract_methods
            .returns_struct_in_tuple(my_struct_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_struct_tuple);
    }
    {
        // Tuple with enum.
        let my_enum_tuple: (u64, State) = (42, State::A);

        let response = contract_methods
            .returns_enum_in_tuple(my_enum_tuple.clone())
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // Tuple with single element
        let my_enum_tuple = (123u64,);

        let response = contract_methods
            .single_element_tuple(my_enum_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_enum_tuple);
    }
    {
        // tuple with b256
        let id = *ContractId::zeroed();
        let my_b256_u8_tuple = (Bits256(id), 10);

        let response = contract_methods
            .tuple_with_b256(my_b256_u8_tuple)
            .call()
            .await?;

        assert_eq!(response.value, my_b256_u8_tuple);
    }

    Ok(())
}

#[tokio::test]
async fn test_evm_address() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/evm_address"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        // ANCHOR: evm_address_arg
        let b256 = Bits256::from_hex_str(
            "0x1616060606060606060606060606060606060606060606060606060606060606",
        )?;
        let evm_address = EvmAddress::from(b256);

        let call_handler = contract_instance
            .methods()
            .evm_address_as_input(evm_address);
        // ANCHOR_END: evm_address_arg

        assert!(call_handler.call().await?.value);
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_literal()
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    {
        let b256 = Bits256::from_hex_str(
            "0x0606060606060606060606060606060606060606060606060606060606060606",
        )?;
        let expected_evm_address = EvmAddress::from(b256);

        assert_eq!(
            contract_instance
                .methods()
                .evm_address_from_argument(b256)
                .call()
                .await?
                .value,
            expected_evm_address
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        contract_instance
            .methods()
            .get_array([42; 2])
            .call()
            .await?
            .value,
        [42; 2]
    );
    Ok(())
}

#[tokio::test]
async fn test_arrays_with_custom_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let persons = [
        Person {
            name: "John".try_into()?,
        },
        Person {
            name: "Jane".try_into()?,
        },
    ];

    let contract_methods = contract_instance.methods();
    let response = contract_methods.array_of_structs(persons).call().await?;

    assert_eq!("John", response.value[0].name);
    assert_eq!("Jane", response.value[1].name);

    let states = [State::A, State::B];

    let response = contract_methods
        .array_of_enums(states.clone())
        .call()
        .await?;

    assert_eq!(states[0], response.value[0]);
    assert_eq!(states[1], response.value[1]);
    Ok(())
}

#[tokio::test]
async fn str_in_array() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/str_in_array"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let input = ["foo", "bar", "baz"].map(|str| str.try_into().unwrap());
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .take_array_string_shuffle(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["baz", "foo", "bar"]);

    let response = contract_methods
        .take_array_string_return_single(input.clone())
        .call()
        .await?;

    assert_eq!(response.value, ["foo"]);

    let response = contract_methods
        .take_array_string_return_single_element(input)
        .call()
        .await?;

    assert_eq!(response.value, "bar");
    Ok(())
}

#[tokio::test]
async fn test_enum_inside_struct() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_inside_struct"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = Cocktail {
        the_thing_you_mix_in: Shaker::Mojito(11),
        glass: 333,
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .return_enum_inside_struct(11)
        .call()
        .await?;

    assert_eq!(response.value, expected);

    let enum_inside_struct = Cocktail {
        the_thing_you_mix_in: Shaker::Cosmopolitan(444),
        glass: 555,
    };

    let response = contract_methods
        .take_enum_inside_struct(enum_inside_struct)
        .call()
        .await?;

    assert_eq!(response.value, 555);
    Ok(())
}

#[tokio::test]
async fn native_types_support() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/native_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let user = User {
        weight: 10,
        address: Address::zeroed(),
    };

    let contract_methods = contract_instance.methods();
    let response = contract_methods.wrapped_address(user).call().await?;

    assert_eq!(response.value.address, Address::zeroed());

    let response = contract_methods
        .unwrapped_address(Address::zeroed())
        .call()
        .await?;

    assert_eq!(
        response.value,
        Address::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")?
    );

    Ok(())
}

#[tokio::test]
async fn enum_coding_w_variable_width_variants() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of enum encoding width, then we'll
    // probably end up mangling arg_2 and onward which will fail this test.
    let expected = BigBundle {
        arg_1: EnumThatHasABigAndSmallVariant::Small(12345),
        arg_2: 6666,
        arg_3: 7777,
        arg_4: 8888,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_big_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_big_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_coding_w_unit_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_encoding"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // If we had a regression on the issue of unit enum encoding width, then
    // we'll end up mangling arg_2
    let expected = UnitBundle {
        arg_1: UnitEnum::var2,
        arg_2: u64::MAX,
    };

    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_unit_bundle().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_bundle_integrity(expected)
        .call()
        .await?
        .value;

    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the bundle correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn enum_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/enum_as_input"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected = MaxedOutVariantsEnum::Variant255(11);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_max_variant().call().await?.value;
    assert_eq!(expected, actual);

    let expected = StandardEnum::Two(12345);
    let contract_methods = contract_instance.methods();
    let actual = contract_methods.get_standard_enum().call().await?.value;
    assert_eq!(expected, actual);

    let fuelvm_judgement = contract_methods
        .check_standard_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the standard enum correctly. Investigate!"
    );

    let expected = UnitEnum::Two;
    let actual = contract_methods.get_unit_enum().call().await?.value;
    assert_eq!(actual, expected);

    let fuelvm_judgement = contract_methods
        .check_unit_enum_integrity(expected)
        .call()
        .await?
        .value;
    assert!(
        fuelvm_judgement,
        "The FuelVM deems that we've not encoded the unit enum correctly. Investigate!"
    );
    Ok(())
}

#[tokio::test]
async fn can_use_try_into_to_construct_enum_from_bytes() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/types/contracts/enum_inside_struct/out/release\
        /enum_inside_struct-abi.json"
    ));
    let shaker_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];

    let expected = Shaker::Mojito(2);

    // as slice
    let actual: Shaker = shaker_in_bytes[..].try_into()?;
    assert_eq!(actual, expected);

    // as ref
    let actual: Shaker = (&shaker_in_bytes).try_into()?;
    assert_eq!(actual, expected);

    // as value
    let actual: Shaker = shaker_in_bytes.try_into()?;
    assert_eq!(actual, expected);
    Ok(())
}

#[tokio::test]
async fn type_inside_enum() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/type_inside_enum"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // String inside enum
    let enum_string = SomeEnum::SomeStr("asdf".try_into()?);
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .str_inside_enum(enum_string.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_string);

    // Array inside enum
    let enum_array = SomeEnum::SomeArr([1, 2, 3, 4]);
    let response = contract_methods
        .arr_inside_enum(enum_array.clone())
        .call()
        .await?;
    assert_eq!(response.value, enum_array);

    // Struct inside enum
    let response = contract_methods
        .return_struct_inside_enum(11)
        .call()
        .await?;
    let expected = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 11 });
    assert_eq!(response.value, expected);

    let struct_inside_enum = Shaker::Cosmopolitan(Recipe { ice: 22, sugar: 66 });
    let response = contract_methods
        .take_struct_inside_enum(struct_inside_enum)
        .call()
        .await?;
    assert_eq!(response.value, 8888);

    // Enum inside enum
    let expected_enum = EnumLevel3::El2(EnumLevel2::El1(EnumLevel1::Num(42)));
    let response = contract_methods.get_nested_enum().call().await?;
    assert_eq!(response.value, expected_enum);

    let response = contract_methods
        .check_nested_enum_integrity(expected_enum)
        .call()
        .await?;
    assert!(
        response.value,
        "The FuelVM deems that we've not encoded the nested enum correctly. Investigate!"
    );

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_some_address = Some(expected_address);
    let response = contract_methods.get_some_address().call().await?;

    assert_eq!(response.value, expected_some_address);

    let expected_some_u64 = Some(10);
    let response = contract_methods.get_some_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_some_struct().call().await?;
    assert_eq!(response.value, Some(s.clone()));

    let response = contract_methods.get_some_enum().call().await?;
    assert_eq!(response.value, Some(e.clone()));

    let response = contract_methods.get_some_tuple().call().await?;
    assert_eq!(response.value, Some((s.clone(), e.clone())));

    let expected_none = None;
    let response = contract_methods.get_none().call().await?;

    assert_eq!(response.value, expected_none);

    Ok(())
}

#[tokio::test]
async fn test_rust_option_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/options"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_u64 = Some(36);
    let response = contract_methods
        .input_primitive(expected_u64)
        .call()
        .await?;

    assert!(response.value);

    let expected_struct = Some(s);
    let response = contract_methods
        .input_struct(expected_struct)
        .call()
        .await?;

    assert!(response.value);

    let expected_enum = Some(e);
    let response = contract_methods.input_enum(expected_enum).call().await?;

    assert!(response.value);

    let expected_none = None;
    let response = contract_methods.input_none(expected_none).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        option: Some(expected_address),
    };

    let e = TestEnum::EnumOption(Some(expected_address));

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods.get_ok_address().call().await?;

    assert_eq!(response.value, expected_ok_address);

    let expected_some_u64 = Ok(10);
    let response = contract_methods.get_ok_u64().call().await?;

    assert_eq!(response.value, expected_some_u64);

    let response = contract_methods.get_ok_struct().call().await?;
    assert_eq!(response.value, Ok(s.clone()));

    let response = contract_methods.get_ok_enum().call().await?;
    assert_eq!(response.value, Ok(e.clone()));

    let response = contract_methods.get_ok_tuple().call().await?;
    assert_eq!(response.value, Ok((s, e)));

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.get_error().call().await?;

    assert_eq!(response.value, expected_error);

    Ok(())
}

#[tokio::test]
async fn test_rust_result_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/results"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let expected_ok_address = Ok(expected_address);
    let response = contract_methods
        .input_ok(expected_ok_address)
        .call()
        .await?;

    assert!(response.value);

    let expected_error = Err(TestError::NoAddress("error".try_into().unwrap()));
    let response = contract_methods.input_error(expected_error).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_decoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_identity_address().call().await?;
    assert_eq!(response.value, Identity::Address(expected_address));

    let response = contract_methods.get_identity_contract_id().call().await?;
    assert_eq!(response.value, Identity::ContractId(expected_contract_id));

    let response = contract_methods.get_struct_with_identity().call().await?;
    assert_eq!(response.value, s.clone());

    let response = contract_methods.get_enum_with_identity().call().await?;
    assert_eq!(response.value, e.clone());

    let response = contract_methods.get_identity_tuple().call().await?;
    assert_eq!(response.value, (s, e));

    Ok(())
}

#[tokio::test]
async fn test_identity_can_be_encoded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;
    let expected_contract_id =
        ContractId::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    let s = TestStruct {
        identity: Identity::Address(expected_address),
    };

    let e = TestEnum::EnumIdentity(Identity::ContractId(expected_contract_id));

    let response = contract_methods
        .input_identity(Identity::Address(expected_address))
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods
        .input_struct_with_identity(s)
        .call()
        .await?;

    assert!(response.value);

    let response = contract_methods.input_enum_with_identity(e).call().await?;

    assert!(response.value);

    Ok(())
}

#[tokio::test]
async fn test_identity_with_two_contracts() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/identity"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance2",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let expected_address =
        Address::from_str("0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0")?;

    {
        let response = contract_instance
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }
    {
        let response = contract_instance2
            .methods()
            .input_identity(Identity::Address(expected_address))
            .call()
            .await?;

        assert!(response.value);
    }

    Ok(())
}

#[tokio::test]
async fn generics_test() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/generics"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: generic
        // simple struct with a single generic param
        let arg1 = SimpleGeneric {
            single_generic_param: 123u64,
        };

        let result = contract_methods
            .struct_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
        // ANCHOR_END: generic
    }
    {
        // struct that delegates the generic param internally
        let arg1 = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "abc".try_into()?,
            },
        };

        let result = contract_methods
            .struct_delegating_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in an array
        let arg1 = StructWArrayGeneric { a: [1u32, 2u32] };

        let result = contract_methods
            .struct_w_generic_in_array(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has a generic struct in an array
        let inner = [
            StructWTwoGenerics {
                a: Bits256([1u8; 32]),
                b: 1,
            },
            StructWTwoGenerics {
                a: Bits256([2u8; 32]),
                b: 2,
            },
            StructWTwoGenerics {
                a: Bits256([3u8; 32]),
                b: 3,
            },
        ];
        let arg1 = StructWArrWGenericStruct { a: inner };

        let result = contract_methods
            .array_with_generic_struct(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // struct that has the generic in a tuple
        let arg1 = StructWTupleGeneric { a: (1, 2) };

        let result = contract_methods
            .struct_w_generic_in_tuple(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        // enum with generic in variant
        let arg1 = EnumWGeneric::B(10);
        let result = contract_methods
            .enum_w_generic(arg1.clone())
            .call()
            .await?
            .value;

        assert_eq!(result, arg1);
    }
    {
        contract_methods
            .unused_generic_args(StructUnusedGeneric::new(15), EnumUnusedGeneric::One(15))
            .call()
            .await?;

        let (the_struct, the_enum) = contract_methods
            .used_and_unused_generic_args(
                StructUsedAndUnusedGenericParams::new(10u8),
                EnumUsedAndUnusedGenericParams::Two(11u8),
            )
            .call()
            .await?
            .value;

        assert_eq!(the_struct.field, 12u8);
        if let EnumUsedAndUnusedGenericParams::Two(val) = the_enum {
            assert_eq!(val, 13)
        } else {
            panic!("Expected the variant EnumUsedAndUnusedGenericParams::Two");
        }
    }
    {
        // complex case
        let pass_through = PassTheGenericOn {
            one: SimpleGeneric {
                single_generic_param: "ab".try_into()?,
            },
        };
        let w_arr_generic = StructWArrayGeneric {
            a: [pass_through.clone(), pass_through],
        };

        let arg1 = MegaExample {
            a: ([Bits256([0; 32]), Bits256([0; 32])], "ab".try_into()?),
            b: vec![(
                [EnumWGeneric::B(StructWTupleGeneric {
                    a: (w_arr_generic.clone(), w_arr_generic),
                })],
                10u32,
            )],
        };
        contract_methods.complex_test(arg1.clone()).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_vectors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/vectors"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let methods = contract_instance.methods();

    {
        // vec of u32s
        let arg = vec![0, 1, 2];
        methods.u32_vec(arg).call().await?;
    }
    {
        // vec of vecs of u32s
        let arg = vec![vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_vec(arg.clone()).call().await?;
    }
    {
        // vec of structs
        // ANCHOR: passing_in_vec
        let arg = vec![SomeStruct { a: 0 }, SomeStruct { a: 1 }];
        methods.struct_in_vec(arg.clone()).call().await?;
        // ANCHOR_END: passing_in_vec
    }
    {
        // vec in struct
        let arg = SomeStruct { a: vec![0, 1, 2] };
        methods.vec_in_struct(arg.clone()).call().await?;
    }
    {
        // array in vec
        let arg = vec![[0u64, 1u64], [0u64, 1u64]];
        methods.array_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in array
        let arg = [vec![0, 1, 2], vec![0, 1, 2]];
        methods.vec_in_array(arg.clone()).call().await?;
    }
    {
        // vec in enum
        let arg = SomeEnum::a(vec![0, 1, 2]);
        methods.vec_in_enum(arg.clone()).call().await?;
    }
    {
        // enum in vec
        let arg = vec![SomeEnum::a(0), SomeEnum::a(1)];
        methods.enum_in_vec(arg.clone()).call().await?;
    }
    {
        // tuple in vec
        let arg = vec![(0, 0), (1, 1)];
        methods.tuple_in_vec(arg.clone()).call().await?;
    }
    {
        // vec in tuple
        let arg = (vec![0, 1, 2], vec![0, 1, 2]);
        methods.vec_in_tuple(arg.clone()).call().await?;
    }
    {
        // vec in a vec in a struct in a vec
        let arg = vec![
            SomeStruct {
                a: vec![vec![0, 1, 2], vec![3, 4, 5]],
            },
            SomeStruct {
                a: vec![vec![6, 7, 8], vec![9, 10, 11]],
            },
        ];
        methods
            .vec_in_a_vec_in_a_struct_in_a_vec(arg.clone())
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_b256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(
        Bits256([2; 32]),
        contract_instance
            .methods()
            .b256_as_output()
            .call()
            .await?
            .value
    );

    {
        // ANCHOR: 256_arg
        let b256 = Bits256([1; 32]);

        let call_handler = contract_instance.methods().b256_as_input(b256);
        // ANCHOR_END: 256_arg

        assert!(call_handler.call().await?.value);
    }

    Ok(())
}

#[tokio::test]
async fn test_b512() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/b512"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: b512_example
    let hi_bits = Bits256::from_hex_str(
        "0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c",
    )?;
    let lo_bits = Bits256::from_hex_str(
        "0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
    )?;
    let b512 = B512::from((hi_bits, lo_bits));
    // ANCHOR_END: b512_example

    assert_eq!(b512, contract_methods.b512_as_output().call().await?.value);

    {
        let lo_bits2 = Bits256::from_hex_str(
            "0x54ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d",
        )?;
        let b512 = B512::from((hi_bits, lo_bits2));

        assert!(contract_methods.b512_as_input(b512).call().await?.value);
    }

    Ok(())
}

fn u128_from(parts: (u64, u64)) -> u128 {
    let bytes: [u8; 16] = [parts.0.to_be_bytes(), parts.1.to_be_bytes()]
        .concat()
        .try_into()
        .unwrap();
    u128::from_be_bytes(bytes)
}

#[tokio::test]
async fn test_u128() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u128"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u128_from((1, 2));

        let actual = contract_methods.u128_sum_and_ret(arg).call().await?.value;

        let expected = arg + u128_from((3, 4));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u128_in_enum_output().call().await?.value;

        let expected = SomeEnum::B(u128_from((4, 4)));
        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u128_from((3, 3)));

        contract_methods.u128_in_enum_input(input).call().await?;
    }

    Ok(())
}

fn u256_from(parts: (u64, u64, u64, u64)) -> U256 {
    let bytes: [u8; 32] = [
        parts.0.to_be_bytes(),
        parts.1.to_be_bytes(),
        parts.2.to_be_bytes(),
        parts.3.to_be_bytes(),
    ]
    .concat()
    .try_into()
    .unwrap();
    U256::from(bytes)
}

#[tokio::test]
async fn test_u256() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TypesContract",
            project = "e2e/sway/types/contracts/u256"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TypesContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();
    {
        let arg = u256_from((1, 2, 3, 4));
        let actual = contract_methods.u256_sum_and_ret(arg).call().await?.value;
        let expected = arg + u256_from((3, 4, 5, 6));

        assert_eq!(expected, actual);
    }
    {
        let actual = contract_methods.u256_in_enum_output().call().await?.value;
        let expected = SomeEnum::B(u256_from((1, 2, 3, 4)));

        assert_eq!(expected, actual);
    }
    {
        let input = SomeEnum::B(u256_from((2, 3, 4, 5)));

        contract_methods.u256_in_enum_input(input).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_base_type_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    // ANCHOR: returning_vec
    let response = contract_methods.u8_in_vec(10).call().await?;
    assert_eq!(response.value, (0..10).collect::<Vec<_>>());
    // ANCHOR_END: returning_vec

    let response = contract_methods.u16_in_vec(11).call().await?;
    assert_eq!(response.value, (0..11).collect::<Vec<_>>());

    let response = contract_methods.u32_in_vec(12).call().await?;
    assert_eq!(response.value, (0..12).collect::<Vec<_>>());

    let response = contract_methods.u64_in_vec(13).call().await?;
    assert_eq!(response.value, (0..13).collect::<Vec<_>>());

    let response = contract_methods.bool_in_vec().call().await?;
    assert_eq!(response.value, [true, false, true, false].to_vec());

    let response = contract_methods.b256_in_vec(13).call().await?;
    assert_eq!(response.value, vec![Bits256([2; 32]); 13]);

    Ok(())
}

#[tokio::test]
async fn test_composite_types_in_vec_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "VectorOutputContract",
            project = "e2e/sway/types/contracts/vector_output"
        )),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let expected: Vec<[u64; 4]> = vec![[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
        let response = contract_methods.array_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    {
        let expected: Vec<Pasta> = vec![
            Pasta::Tortelini(Bimbam {
                bim: 1111,
                bam: 2222_u32,
            }),
            Pasta::Rigatoni(1987),
            Pasta::Spaghetti(true),
        ];

        let response = contract_methods.enum_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<Bimbam> = vec![
            Bimbam {
                bim: 1111,
                bam: 2222_u32,
            },
            Bimbam {
                bim: 3333,
                bam: 4444_u32,
            },
            Bimbam {
                bim: 5555,
                bam: 6666_u32,
            },
        ];
        let response = contract_methods.struct_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<(u64, u32)> = vec![(1111, 2222_u32), (3333, 4444_u32), (5555, 6666_u32)];
        let response = contract_methods.tuple_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }

    {
        let expected: Vec<SizedAsciiString<4>> =
            vec!["hell".try_into()?, "ello".try_into()?, "lloh".try_into()?];
        let response = contract_methods.str_in_vec().call().await?.value;
        assert_eq!(response, expected);
    }
    Ok(())
}

#[tokio::test]
async fn test_bytes_output() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesOutputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let response = contract_methods.return_bytes(10).call().await?;

    assert_eq!(response.value, (0..10).collect::<Vec<_>>());

    Ok(())
}

#[tokio::test]
async fn test_bytes_as_input() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BytesInputContract",
            project = "e2e/sway/types/contracts/bytes"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BytesInputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        // ANCHOR: bytes_arg
        let bytes = Bytes(vec![40, 41, 42]);

        contract_methods.accept_bytes(bytes).call().await?;
        // ANCHOR_END: bytes_arg
    }
    {
        let bytes = Bytes(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![bytes.clone(), bytes.clone()],
            inner_enum: SomeEnum::Second(bytes),
        };

        contract_methods.accept_nested_bytes(wrapper).call().await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_raw_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RawSliceContract",
            project = "e2e/sway/types/contracts/raw_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RawSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    {
        for length in 0u8..=10 {
            let response = contract_methods.return_raw_slice(length).call().await?;
            assert_eq!(response.value, (0u8..length).collect::<Vec<u8>>());
        }
    }
    {
        contract_methods
            .accept_raw_slice(RawSlice(vec![40, 41, 42]))
            .call()
            .await?;
    }
    {
        let raw_slice = RawSlice(vec![40, 41, 42]);
        let wrapper = Wrapper {
            inner: vec![raw_slice.clone(), raw_slice.clone()],
            inner_enum: SomeEnum::Second(raw_slice),
        };

        contract_methods
            .accept_nested_raw_slice(wrapper)
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn contract_string_slice() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StringSliceContract",
            project = "e2e/sway/types/contracts/string_slice"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StringSliceContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .handles_str("contract-input".try_into()?)
        .call()
        .await?;
    assert_eq!(response.value, "contract-return");

    Ok(())
}

#[tokio::test]
async fn contract_std_lib_string() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "StdLibString",
            project = "e2e/sway/types/contracts/std_lib_string"
        )),
        Deploy(
            name = "contract_instance",
            contract = "StdLibString",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.return_dynamic_string().call().await?.value;
        assert_eq!(resp, "Hello World");
    }
    {
        let _resp = contract_methods
            .accepts_dynamic_string(String::from("Hello World"))
            .call()
            .await?;
    }
    {
        // confirm encoding/decoding a string wasn't faulty and led to too high gas consumption
        let _resp = contract_methods
            .echoes_dynamic_string(String::from("Hello Fuel"))
            .with_tx_policies(TxPolicies::default().with_script_gas_limit(3600))
            .call()
            .await?;
    }

    Ok(())
}

#[tokio::test]
async fn test_heap_type_in_enums() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_type_in_enums"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    {
        let resp = contract_methods.returns_bytes_result(true).call().await?;
        let expected = Ok(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_result(false).call().await?;
        let expected = Err(TestError::Something([255u8, 255u8, 255u8, 255u8, 255u8]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(true).call().await?;
        let expected = Ok(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_result(false).call().await?;
        let expected = Err(TestError::Else(7777));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(true).call().await?;
        let expected = Ok("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_str_result(true).call().await?;
        let expected = Ok("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_result(false).call().await?;
        let expected = Err(TestError::Else(3333));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(true).call().await?;
        let expected = Some(Bytes(vec![1, 1, 1, 1]));

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_bytes_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_vec_option(true).call().await?;
        let expected = Some(vec![2, 2, 2, 2, 2]);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_vec_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_string_option(true).call().await?;
        let expected = Some("Hello World".to_string());

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }
    {
        let resp = contract_methods.returns_str_option(true).call().await?;
        let expected = Some("Hello World".try_into()?);

        assert_eq!(resp.value, expected);
    }
    {
        let resp = contract_methods.returns_string_option(false).call().await?;

        assert!(resp.value.is_none());
    }

    Ok(())
}

#[tokio::test]
async fn nested_heap_types() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "HeapTypeInEnum",
            project = "e2e/sway/types/contracts/heap_types"
        )),
        Deploy(
            name = "contract_instance",
            contract = "HeapTypeInEnum",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let arr = [2u8, 4, 8];
    let struct_generics = StructGenerics {
        one: Bytes(arr.to_vec()),
        two: String::from("fuel"),
        three: RawSlice(arr.to_vec()),
    };

    let enum_vec = [struct_generics.clone(), struct_generics].to_vec();
    let expected = EnumGeneric::One(enum_vec);

    let result = contract_instance
        .methods()
        .nested_heap_types()
        .call()
        .await?;

    assert_eq!(result.value, expected);

    Ok(())
}\n```

Convert to EvmAddress

Convert a Bits256 address to an EvmAddress:

```rust\n#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use fuels::{
        prelude::Result,
        types::{Bits256, EvmAddress, Identity},
    };

    #[tokio::test]
    async fn bytes32() -> Result<()> {
        // ANCHOR: bytes32
        use std::str::FromStr;

        use fuels::types::Bytes32;

        // Zeroed Bytes32
        let b256 = Bytes32::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *b256);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_bytes32
        let my_slice = [1u8; 32];
        let b256 = Bytes32::new(my_slice);
        // ANCHOR_END: array_to_bytes32
        assert_eq!([1u8; 32], *b256);

        // From a hex string.
        // ANCHOR: hex_string_to_bytes32
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let b256 = Bytes32::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_bytes32
        assert_eq!([0u8; 32], *b256);
        // ANCHOR_END: bytes32

        // ANCHOR: bytes32_format
        let b256_string = b256.to_string();
        let b256_hex_string = format!("{b256:#x}");
        // ANCHOR_END: bytes32_format

        assert_eq!(hex_str[2..], b256_string);
        assert_eq!(hex_str, b256_hex_string);

        // ANCHOR: bytes32_to_str
        let _str_from_bytes32: &str = b256.to_string().as_str();
        // ANCHOR_END: bytes32_to_str

        Ok(())
    }
    #[tokio::test]
    async fn address() -> Result<()> {
        // ANCHOR: address
        use std::str::FromStr;

        use fuels::types::Address;

        // Zeroed Bytes32
        let address = Address::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *address);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_address
        let my_slice = [1u8; 32];
        let address = Address::new(my_slice);
        // ANCHOR_END: array_to_address
        assert_eq!([1u8; 32], *address);

        // From a string.
        // ANCHOR: hex_string_to_address
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let address = Address::from_str(hex_str)?;
        // ANCHOR_END: hex_string_to_address
        assert_eq!([0u8; 32], *address);
        // ANCHOR_END: address

        // ANCHOR: address_to_identity
        let _identity_from_address = Identity::Address(address);
        // ANCHOR_END: address_to_identity

        // ANCHOR: address_to_str
        let _str_from_address: &str = address.to_string().as_str();
        // ANCHOR_END: address_to_str

        // ANCHOR: address_to_bits256
        let bits_256 = Bits256(address.into());
        // ANCHOR_END: address_to_bits256

        // ANCHOR: b256_to_evm_address
        let _evm_address = EvmAddress::from(bits_256);
        // ANCHOR_END: b256_to_evm_address

        Ok(())
    }
    #[tokio::test]
    async fn bech32() -> Result<()> {
        // ANCHOR: bech32
        use fuels::types::{bech32::Bech32Address, Address, Bytes32};

        // New from HRP string and a hash
        // ANCHOR: array_to_bech32
        let hrp = "fuel";
        let my_slice = [1u8; 32];
        let _bech32_address = Bech32Address::new(hrp, my_slice);
        // ANCHOR_END: array_to_bech32

        // Note that you can also pass a hash stored as Bytes32 to new:
        // ANCHOR: bytes32_to_bech32
        let my_hash = Bytes32::new([1u8; 32]);
        let _bech32_address = Bech32Address::new(hrp, my_hash);
        // ANCHOR_END: bytes32_to_bech32

        // From a string.
        // ANCHOR: str_to_bech32
        let address = "fuel1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsx2mt2";
        let bech32_address = Bech32Address::from_str(address)?;
        // ANCHOR_END: str_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // From Address
        // ANCHOR: address_to_bech32
        let plain_address = Address::new([0u8; 32]);
        let bech32_address = Bech32Address::from(plain_address);
        // ANCHOR_END: address_to_bech32
        assert_eq!([0u8; 32], *bech32_address.hash());

        // Convert to Address
        // ANCHOR: bech32_to_address
        let _plain_address: Address = bech32_address.into();
        // ANCHOR_END: bech32_to_address

        // ANCHOR_END: bech32

        Ok(())
    }
    #[tokio::test]
    async fn asset_id() -> Result<()> {
        // ANCHOR: asset_id
        use std::str::FromStr;

        use fuels::types::AssetId;

        // Zeroed Bytes32
        let asset_id = AssetId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *asset_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_asset_id
        let my_slice = [1u8; 32];
        let asset_id = AssetId::new(my_slice);
        // ANCHOR_END: array_to_asset_id
        assert_eq!([1u8; 32], *asset_id);

        // From a string.
        // ANCHOR: string_to_asset_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let asset_id = AssetId::from_str(hex_str)?;
        // ANCHOR_END: string_to_asset_id
        assert_eq!([0u8; 32], *asset_id);
        // ANCHOR_END: asset_id
        Ok(())
    }
    #[tokio::test]
    async fn contract_id() -> Result<()> {
        // ANCHOR: contract_id
        use std::str::FromStr;

        use fuels::types::ContractId;

        // Zeroed Bytes32
        let contract_id = ContractId::zeroed();

        // Grab the inner `[u8; 32]` from
        // `Bytes32` by dereferencing (i.e. `*`) it.
        assert_eq!([0u8; 32], *contract_id);

        // From a `[u8; 32]`.
        // ANCHOR: array_to_contract_id
        let my_slice = [1u8; 32];
        let contract_id = ContractId::new(my_slice);
        // ANCHOR_END: array_to_contract_id
        assert_eq!([1u8; 32], *contract_id);

        // From a string.
        // ANCHOR: string_to_contract_id
        let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000000";
        let contract_id = ContractId::from_str(hex_str)?;
        // ANCHOR_END: string_to_contract_id
        assert_eq!([0u8; 32], *contract_id);
        // ANCHOR_END: contract_id

        // ANCHOR: contract_id_to_identity
        let _identity_from_contract_id = Identity::ContractId(contract_id);
        // ANCHOR_END: contract_id_to_identity

        // ANCHOR: contract_id_to_str
        let _str_from_contract_id: &str = contract_id.to_string().as_str();
        // ANCHOR_END: contract_id_to_str

        Ok(())
    }

    #[tokio::test]
    async fn type_conversion() -> Result<()> {
        // ANCHOR: type_conversion
        use fuels::types::{AssetId, ContractId};

        let contract_id = ContractId::new([1u8; 32]);

        let asset_id: AssetId = AssetId::new(*contract_id);

        assert_eq!([1u8; 32], *asset_id);
        // ANCHOR_END: type_conversion

        // ANCHOR: asset_id_to_str
        let _str_from_asset_id: &str = asset_id.to_string().as_str();
        // ANCHOR_END: asset_id_to_str

        // ANCHOR: contract_id_to_bits256
        let _contract_id_to_bits_256 = Bits256(contract_id.into());
        // ANCHOR_END: contract_id_to_bits256

        // ANCHOR: asset_id_to_bits256
        let _asset_id_to_bits_256 = Bits256(asset_id.into());
        // ANCHOR_END: asset_id_to_bits256

        Ok(())
    }

    #[tokio::test]
    async fn unused_generics() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        // ANCHOR: unused_generics_struct
        assert_eq!(
            <StructUnusedGeneric<u16, u32>>::new(15),
            StructUnusedGeneric {
                field: 15,
                _unused_generic_0: std::marker::PhantomData,
                _unused_generic_1: std::marker::PhantomData
            }
        );
        // ANCHOR_END: unused_generics_struct

        let my_enum = <EnumUnusedGeneric<u32, u64>>::One(15);
        // ANCHOR: unused_generics_enum
        match my_enum {
            EnumUnusedGeneric::One(_value) => {}
            EnumUnusedGeneric::IgnoreMe(..) => panic!("Will never receive this variant"),
        }
        // ANCHOR_END: unused_generics_enum

        Ok(())
    }
}\n```

Codec

Encoding and decoding are done as per the fuel spec. To this end, fuels makes use of the ABIEncoder and the ABIDecoder.

Prerequisites for decoding/encoding

To encode a type, you must first convert it into a Token. This is commonly done by implementing the Tokenizable trait.

To decode, you also need to provide a ParamType describing the schema of the type in question. This is commonly done by implementing the Parameterize trait.

All types generated by the abigen! macro implement both the Tokenizable and Parameterize traits.

fuels also contains implementations for:

Deriving the traits

Both Tokenizable and Parameterize can be derived for structs and enums if all inner types implement the derived traits:

```rust\nextern crate alloc;

#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[test]
    fn example_of_abigen_usage() {
        // ANCHOR: multiple_abigen_program_types
        abigen!(
            Contract(
                name = "ContractA",
                abi = "e2e/sway/bindings/sharing_types/contract_a/out/release/contract_a-abi.json"
            ),
            Contract(
                name = "ContractB",
                abi = "e2e/sway/bindings/sharing_types/contract_b/out/release/contract_b-abi.json"
            ),
            Script(
                name = "MyScript",
                abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
            ),
            Predicate(
                name = "MyPredicateEncoder",
                abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
            ),
        );
        // ANCHOR_END: multiple_abigen_program_types
    }

    #[test]
    fn macro_deriving() {
        // ANCHOR: deriving_traits
        use fuels::macros::{Parameterize, Tokenizable};

        #[derive(Parameterize, Tokenizable)]
        struct MyStruct {
            field_a: u8,
        }

        #[derive(Parameterize, Tokenizable)]
        enum SomeEnum {
            A(MyStruct),
            B(Vec<u64>),
        }
        // ANCHOR_END: deriving_traits
    }
    #[test]
    fn macro_deriving_extra() {
        {
            use fuels::{
                core as fuels_core_elsewhere,
                macros::{Parameterize, Tokenizable},
                types as fuels_types_elsewhere,
            };

            // ANCHOR: deriving_traits_paths
            #[derive(Parameterize, Tokenizable)]
            #[FuelsCorePath = "fuels_core_elsewhere"]
            #[FuelsTypesPath = "fuels_types_elsewhere"]
            pub struct SomeStruct {
                field_a: u64,
            }
            // ANCHOR_END: deriving_traits_paths
        }
        {
            // ANCHOR: deriving_traits_nostd
            use fuels::macros::{Parameterize, Tokenizable};
            #[derive(Parameterize, Tokenizable)]
            #[NoStd]
            pub struct SomeStruct {
                field_a: u64,
            }
            // ANCHOR_END: deriving_traits_nostd
        }
    }
}\n```

Note: Deriving Tokenizable on enums requires that all variants also implement Parameterize.

Tweaking the derivation

Changing the location of imports

The derived code expects that the fuels package is accessible through ::fuels. If this is not the case then the derivation macro needs to be given the locations of fuels::types and fuels::core.

```rust\nextern crate alloc;

#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[test]
    fn example_of_abigen_usage() {
        // ANCHOR: multiple_abigen_program_types
        abigen!(
            Contract(
                name = "ContractA",
                abi = "e2e/sway/bindings/sharing_types/contract_a/out/release/contract_a-abi.json"
            ),
            Contract(
                name = "ContractB",
                abi = "e2e/sway/bindings/sharing_types/contract_b/out/release/contract_b-abi.json"
            ),
            Script(
                name = "MyScript",
                abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
            ),
            Predicate(
                name = "MyPredicateEncoder",
                abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
            ),
        );
        // ANCHOR_END: multiple_abigen_program_types
    }

    #[test]
    fn macro_deriving() {
        // ANCHOR: deriving_traits
        use fuels::macros::{Parameterize, Tokenizable};

        #[derive(Parameterize, Tokenizable)]
        struct MyStruct {
            field_a: u8,
        }

        #[derive(Parameterize, Tokenizable)]
        enum SomeEnum {
            A(MyStruct),
            B(Vec<u64>),
        }
        // ANCHOR_END: deriving_traits
    }
    #[test]
    fn macro_deriving_extra() {
        {
            use fuels::{
                core as fuels_core_elsewhere,
                macros::{Parameterize, Tokenizable},
                types as fuels_types_elsewhere,
            };

            // ANCHOR: deriving_traits_paths
            #[derive(Parameterize, Tokenizable)]
            #[FuelsCorePath = "fuels_core_elsewhere"]
            #[FuelsTypesPath = "fuels_types_elsewhere"]
            pub struct SomeStruct {
                field_a: u64,
            }
            // ANCHOR_END: deriving_traits_paths
        }
        {
            // ANCHOR: deriving_traits_nostd
            use fuels::macros::{Parameterize, Tokenizable};
            #[derive(Parameterize, Tokenizable)]
            #[NoStd]
            pub struct SomeStruct {
                field_a: u64,
            }
            // ANCHOR_END: deriving_traits_nostd
        }
    }
}\n```

Generating no-std code

If you want no-std generated code:

```rust\nextern crate alloc;

#[cfg(test)]
mod tests {
    use fuels::prelude::*;

    #[test]
    fn example_of_abigen_usage() {
        // ANCHOR: multiple_abigen_program_types
        abigen!(
            Contract(
                name = "ContractA",
                abi = "e2e/sway/bindings/sharing_types/contract_a/out/release/contract_a-abi.json"
            ),
            Contract(
                name = "ContractB",
                abi = "e2e/sway/bindings/sharing_types/contract_b/out/release/contract_b-abi.json"
            ),
            Script(
                name = "MyScript",
                abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
            ),
            Predicate(
                name = "MyPredicateEncoder",
                abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
            ),
        );
        // ANCHOR_END: multiple_abigen_program_types
    }

    #[test]
    fn macro_deriving() {
        // ANCHOR: deriving_traits
        use fuels::macros::{Parameterize, Tokenizable};

        #[derive(Parameterize, Tokenizable)]
        struct MyStruct {
            field_a: u8,
        }

        #[derive(Parameterize, Tokenizable)]
        enum SomeEnum {
            A(MyStruct),
            B(Vec<u64>),
        }
        // ANCHOR_END: deriving_traits
    }
    #[test]
    fn macro_deriving_extra() {
        {
            use fuels::{
                core as fuels_core_elsewhere,
                macros::{Parameterize, Tokenizable},
                types as fuels_types_elsewhere,
            };

            // ANCHOR: deriving_traits_paths
            #[derive(Parameterize, Tokenizable)]
            #[FuelsCorePath = "fuels_core_elsewhere"]
            #[FuelsTypesPath = "fuels_types_elsewhere"]
            pub struct SomeStruct {
                field_a: u64,
            }
            // ANCHOR_END: deriving_traits_paths
        }
        {
            // ANCHOR: deriving_traits_nostd
            use fuels::macros::{Parameterize, Tokenizable};
            #[derive(Parameterize, Tokenizable)]
            #[NoStd]
            pub struct SomeStruct {
                field_a: u64,
            }
            // ANCHOR_END: deriving_traits_nostd
        }
    }
}\n```

Encoding

Be sure to read the prerequisites to encoding.

Encoding is done via the ABIEncoder:

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        core::codec::{DecoderConfig, EncoderConfig},
        types::errors::Result,
    };

    #[test]
    fn encoding_a_type() -> Result<()> {
        //ANCHOR: encoding_example
        use fuels::{
            core::{codec::ABIEncoder, traits::Tokenizable},
            macros::Tokenizable,
        };

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let instance = MyStruct { field: 101 };
        let _encoded: Vec<u8> = ABIEncoder::default().encode(&[instance.into_token()])?;
        //ANCHOR_END: encoding_example

        Ok(())
    }
    #[test]
    fn encoding_via_macro() -> Result<()> {
        //ANCHOR: encoding_example_w_macro
        use fuels::{core::codec::calldata, macros::Tokenizable};

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }
        let _: Vec<u8> = calldata!(MyStruct { field: 101 }, MyStruct { field: 102 })?;
        //ANCHOR_END: encoding_example_w_macro

        Ok(())
    }

    #[test]
    fn decoding_example() -> Result<()> {
        // ANCHOR: decoding_example
        use fuels::{
            core::{
                codec::ABIDecoder,
                traits::{Parameterize, Tokenizable},
            },
            macros::{Parameterize, Tokenizable},
            types::Token,
        };

        #[derive(Parameterize, Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let token: Token = ABIDecoder::default().decode(&MyStruct::param_type(), bytes)?;

        let _: MyStruct = MyStruct::from_token(token)?;
        // ANCHOR_END: decoding_example

        Ok(())
    }

    #[test]
    fn decoding_example_try_into() -> Result<()> {
        // ANCHOR: decoding_example_try_into
        use fuels::macros::{Parameterize, Tokenizable, TryFrom};

        #[derive(Parameterize, Tokenizable, TryFrom)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let _: MyStruct = bytes.try_into()?;
        // ANCHOR_END: decoding_example_try_into

        Ok(())
    }

    #[test]
    fn configuring_the_decoder() -> Result<()> {
        // ANCHOR: configuring_the_decoder

        use fuels::core::codec::ABIDecoder;

        ABIDecoder::new(DecoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_decoder

        Ok(())
    }

    #[test]
    fn configuring_the_encoder() -> Result<()> {
        // ANCHOR: configuring_the_encoder
        use fuels::core::codec::ABIEncoder;

        ABIEncoder::new(EncoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_encoder

        Ok(())
    }
}\n```

There is also a shortcut-macro that can encode multiple types which implement Tokenizable:

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        core::codec::{DecoderConfig, EncoderConfig},
        types::errors::Result,
    };

    #[test]
    fn encoding_a_type() -> Result<()> {
        //ANCHOR: encoding_example
        use fuels::{
            core::{codec::ABIEncoder, traits::Tokenizable},
            macros::Tokenizable,
        };

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let instance = MyStruct { field: 101 };
        let _encoded: Vec<u8> = ABIEncoder::default().encode(&[instance.into_token()])?;
        //ANCHOR_END: encoding_example

        Ok(())
    }
    #[test]
    fn encoding_via_macro() -> Result<()> {
        //ANCHOR: encoding_example_w_macro
        use fuels::{core::codec::calldata, macros::Tokenizable};

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }
        let _: Vec<u8> = calldata!(MyStruct { field: 101 }, MyStruct { field: 102 })?;
        //ANCHOR_END: encoding_example_w_macro

        Ok(())
    }

    #[test]
    fn decoding_example() -> Result<()> {
        // ANCHOR: decoding_example
        use fuels::{
            core::{
                codec::ABIDecoder,
                traits::{Parameterize, Tokenizable},
            },
            macros::{Parameterize, Tokenizable},
            types::Token,
        };

        #[derive(Parameterize, Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let token: Token = ABIDecoder::default().decode(&MyStruct::param_type(), bytes)?;

        let _: MyStruct = MyStruct::from_token(token)?;
        // ANCHOR_END: decoding_example

        Ok(())
    }

    #[test]
    fn decoding_example_try_into() -> Result<()> {
        // ANCHOR: decoding_example_try_into
        use fuels::macros::{Parameterize, Tokenizable, TryFrom};

        #[derive(Parameterize, Tokenizable, TryFrom)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let _: MyStruct = bytes.try_into()?;
        // ANCHOR_END: decoding_example_try_into

        Ok(())
    }

    #[test]
    fn configuring_the_decoder() -> Result<()> {
        // ANCHOR: configuring_the_decoder

        use fuels::core::codec::ABIDecoder;

        ABIDecoder::new(DecoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_decoder

        Ok(())
    }

    #[test]
    fn configuring_the_encoder() -> Result<()> {
        // ANCHOR: configuring_the_encoder
        use fuels::core::codec::ABIEncoder;

        ABIEncoder::new(EncoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_encoder

        Ok(())
    }
}\n```

Configuring the encoder

The encoder can be configured to limit its resource expenditure:

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        core::codec::{DecoderConfig, EncoderConfig},
        types::errors::Result,
    };

    #[test]
    fn encoding_a_type() -> Result<()> {
        //ANCHOR: encoding_example
        use fuels::{
            core::{codec::ABIEncoder, traits::Tokenizable},
            macros::Tokenizable,
        };

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let instance = MyStruct { field: 101 };
        let _encoded: Vec<u8> = ABIEncoder::default().encode(&[instance.into_token()])?;
        //ANCHOR_END: encoding_example

        Ok(())
    }
    #[test]
    fn encoding_via_macro() -> Result<()> {
        //ANCHOR: encoding_example_w_macro
        use fuels::{core::codec::calldata, macros::Tokenizable};

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }
        let _: Vec<u8> = calldata!(MyStruct { field: 101 }, MyStruct { field: 102 })?;
        //ANCHOR_END: encoding_example_w_macro

        Ok(())
    }

    #[test]
    fn decoding_example() -> Result<()> {
        // ANCHOR: decoding_example
        use fuels::{
            core::{
                codec::ABIDecoder,
                traits::{Parameterize, Tokenizable},
            },
            macros::{Parameterize, Tokenizable},
            types::Token,
        };

        #[derive(Parameterize, Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let token: Token = ABIDecoder::default().decode(&MyStruct::param_type(), bytes)?;

        let _: MyStruct = MyStruct::from_token(token)?;
        // ANCHOR_END: decoding_example

        Ok(())
    }

    #[test]
    fn decoding_example_try_into() -> Result<()> {
        // ANCHOR: decoding_example_try_into
        use fuels::macros::{Parameterize, Tokenizable, TryFrom};

        #[derive(Parameterize, Tokenizable, TryFrom)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let _: MyStruct = bytes.try_into()?;
        // ANCHOR_END: decoding_example_try_into

        Ok(())
    }

    #[test]
    fn configuring_the_decoder() -> Result<()> {
        // ANCHOR: configuring_the_decoder

        use fuels::core::codec::ABIDecoder;

        ABIDecoder::new(DecoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_decoder

        Ok(())
    }

    #[test]
    fn configuring_the_encoder() -> Result<()> {
        // ANCHOR: configuring_the_encoder
        use fuels::core::codec::ABIEncoder;

        ABIEncoder::new(EncoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_encoder

        Ok(())
    }
}\n```

The default values for the EncoderConfig are:

```rust\nmod bounded_encoder;

use std::default::Default;

use crate::{
    codec::abi_encoder::bounded_encoder::BoundedEncoder,
    types::{errors::Result, Token},
};

#[derive(Debug, Clone, Copy)]
pub struct EncoderConfig {
    /// Entering a struct, array, tuple, enum or vector increases the depth. Encoding will fail if
    /// the current depth becomes greater than `max_depth` configured here.
    pub max_depth: usize,
    /// Every encoded argument will increase the token count. Encoding will fail if the current
    /// token count becomes greater than `max_tokens` configured here.
    pub max_tokens: usize,
}

// ANCHOR: default_encoder_config
impl Default for EncoderConfig {
    fn default() -> Self {
        Self {
            max_depth: 45,
            max_tokens: 10_000,
        }
    }
}
// ANCHOR_END: default_encoder_config

#[derive(Default, Clone, Debug)]
pub struct ABIEncoder {
    pub config: EncoderConfig,
}

impl ABIEncoder {
    pub fn new(config: EncoderConfig) -> Self {
        Self { config }
    }

    /// Encodes `Token`s following the ABI specs defined
    /// [here](https://github.com/FuelLabs/fuel-specs/blob/master/specs/protocol/abi.md)
    pub fn encode(&self, tokens: &[Token]) -> Result<Vec<u8>> {
        BoundedEncoder::new(self.config).encode(tokens)
    }
}

#[cfg(test)]
mod tests {
    use std::slice;

    use super::*;
    use crate::{
        to_named,
        types::{
            errors::Error,
            param_types::{EnumVariants, ParamType},
            StaticStringToken, U256,
        },
    };

    #[test]
    fn encode_multiple_uint() -> Result<()> {
        let tokens = [
            Token::U8(u8::MAX),
            Token::U16(u16::MAX),
            Token::U32(u32::MAX),
            Token::U64(u64::MAX),
            Token::U128(u128::MAX),
            Token::U256(U256::MAX),
        ];

        let result = ABIEncoder::default().encode(&tokens)?;

        let expected = [
            255, // u8
            255, 255, // u16
            255, 255, 255, 255, // u32
            255, 255, 255, 255, 255, 255, 255, 255, // u64
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, // u128
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // u256
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_bool() -> Result<()> {
        let token = Token::Bool(true);

        let result = ABIEncoder::default().encode(&[token])?;

        let expected = [1];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_b256() -> Result<()> {
        let data = [
            213, 87, 156, 70, 223, 204, 127, 24, 32, 112, 19, 230, 91, 68, 228, 203, 78, 44, 34,
            152, 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11,
        ];
        let token = Token::B256(data);

        let result = ABIEncoder::default().encode(&[token])?;

        assert_eq!(result, data);

        Ok(())
    }

    #[test]
    fn encode_bytes() -> Result<()> {
        let token = Token::Bytes([255, 0, 1, 2, 3, 4, 5].to_vec());

        let result = ABIEncoder::default().encode(&[token])?;

        let expected = [
            0, 0, 0, 0, 0, 0, 0, 7, // len
            255, 0, 1, 2, 3, 4, 5, // data
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_string() -> Result<()> {
        let token = Token::String("This is a full sentence".to_string());

        let result = ABIEncoder::default().encode(&[token])?;

        let expected = [
            0, 0, 0, 0, 0, 0, 0, 23, // len
            84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110,
            116, 101, 110, 99, 101, //This is a full sentence
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_raw_slice() -> Result<()> {
        let token = Token::RawSlice([255, 0, 1, 2, 3, 4, 5].to_vec());

        let result = ABIEncoder::default().encode(&[token])?;

        let expected = [
            0, 0, 0, 0, 0, 0, 0, 7, // len
            255, 0, 1, 2, 3, 4, 5, // data
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_string_array() -> Result<()> {
        let token = Token::StringArray(StaticStringToken::new(
            "This is a full sentence".into(),
            Some(23),
        ));

        let result = ABIEncoder::default().encode(&[token])?;

        let expected = [
            84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110,
            116, 101, 110, 99, 101, //This is a full sentence
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_string_slice() -> Result<()> {
        let token = Token::StringSlice(StaticStringToken::new(
            "This is a full sentence".into(),
            None,
        ));

        let result = ABIEncoder::default().encode(&[token])?;

        let expected = [
            0, 0, 0, 0, 0, 0, 0, 23, // len
            84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110,
            116, 101, 110, 99, 101, //This is a full sentence
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_tuple() -> Result<()> {
        let token = Token::Tuple(vec![Token::U32(255), Token::Bool(true)]);

        let result = ABIEncoder::default().encode(&[token])?;

        let expected = [
            0, 0, 0, 255, //u32
            1,   //bool
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_array() -> Result<()> {
        let token = Token::Tuple(vec![Token::U32(255), Token::U32(128)]);

        let result = ABIEncoder::default().encode(&[token])?;

        let expected = [
            0, 0, 0, 255, //u32
            0, 0, 0, 128, //u32
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_enum_with_deeply_nested_types() -> Result<()> {
        /*
        enum DeeperEnum {
            v1: bool,
            v2: str[10]
        }
         */
        let types = to_named(&[ParamType::Bool, ParamType::StringArray(10)]);
        let deeper_enum_variants = EnumVariants::new(types)?;
        let deeper_enum_token =
            Token::StringArray(StaticStringToken::new("0123456789".into(), Some(10)));

        /*
        struct StructA {
            some_enum: DeeperEnum
            some_number: u32
        }
         */

        let fields = to_named(&[
            ParamType::Enum {
                name: "".to_string(),
                enum_variants: deeper_enum_variants.clone(),
                generics: vec![],
            },
            ParamType::Bool,
        ]);
        let struct_a_type = ParamType::Struct {
            name: "".to_string(),
            fields,
            generics: vec![],
        };

        let struct_a_token = Token::Struct(vec![
            Token::Enum(Box::new((1, deeper_enum_token, deeper_enum_variants))),
            Token::U32(11332),
        ]);

        /*
         enum TopLevelEnum {
            v1: StructA,
            v2: bool,
            v3: u64
        }
        */

        let types = to_named(&[struct_a_type, ParamType::Bool, ParamType::U64]);
        let top_level_enum_variants = EnumVariants::new(types)?;
        let top_level_enum_token =
            Token::Enum(Box::new((0, struct_a_token, top_level_enum_variants)));

        let result = ABIEncoder::default().encode(slice::from_ref(&top_level_enum_token))?;

        let expected = [
            0, 0, 0, 0, 0, 0, 0, 0, // TopLevelEnum::v1 discriminant
            0, 0, 0, 0, 0, 0, 0, 1, // DeeperEnum::v2 discriminant
            48, 49, 50, 51, 52, 53, 54, 55, 56, 57, // str[10]
            0, 0, 44, 68, // StructA.some_number
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_nested_structs() -> Result<()> {
        let token = Token::Struct(vec![
            Token::U16(10),
            Token::Struct(vec![
                Token::Bool(true),
                Token::Array(vec![Token::U8(1), Token::U8(2)]),
            ]),
        ]);

        let result = ABIEncoder::default().encode(&[token])?;

        let expected = [
            0, 10, // u16
            1,  // bool
            1, 2, // [u8, u8]
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn encode_comprehensive() -> Result<()> {
        let foo = Token::Struct(vec![
            Token::U16(10),
            Token::Struct(vec![
                Token::Bool(true),
                Token::Array(vec![Token::U8(1), Token::U8(2)]),
            ]),
        ]);
        let arr_u8 = Token::Array(vec![Token::U8(1), Token::U8(2)]);
        let b256 = Token::B256([255; 32]);
        let str_arr = Token::StringArray(StaticStringToken::new(
            "This is a full sentence".into(),
            Some(23),
        ));
        let tokens = vec![foo, arr_u8, b256, str_arr];

        let result = ABIEncoder::default().encode(&tokens)?;

        let expected = [
            0, 10, // foo.x == 10u16
            1,  // foo.y.a == true
            1,  // foo.y.b.0 == 1u8
            2,  // foo.y.b.1 == 2u8
            1,  // u8[2].0 == 1u8
            2,  // u8[2].0 == 2u8
            255, 255, 255, 255, 255, 255, 255, 255, // b256
            255, 255, 255, 255, 255, 255, 255, 255, // b256
            255, 255, 255, 255, 255, 255, 255, 255, // b256
            255, 255, 255, 255, 255, 255, 255, 255, // b256
            84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110,
            116, 101, 110, 99, 101, // str[23]
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn enums_with_only_unit_variants_are_encoded_in_one_word() -> Result<()> {
        let expected = [0, 0, 0, 0, 0, 0, 0, 1];

        let types = to_named(&[ParamType::Unit, ParamType::Unit]);
        let enum_selector = Box::new((1, Token::Unit, EnumVariants::new(types)?));

        let actual = ABIEncoder::default().encode(&[Token::Enum(enum_selector)])?;

        assert_eq!(actual, expected);

        Ok(())
    }

    #[test]
    fn vec_in_enum() -> Result<()> {
        // arrange
        let types = to_named(&[ParamType::B256, ParamType::Vector(Box::new(ParamType::U64))]);
        let variants = EnumVariants::new(types)?;
        let selector = (1, Token::Vector(vec![Token::U64(5)]), variants);
        let token = Token::Enum(Box::new(selector));

        // act
        let result = ABIEncoder::default().encode(&[token])?;

        // assert
        let expected = [
            0, 0, 0, 0, 0, 0, 0, 1, // enum dicsriminant
            0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, // vec[len, u64]
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn enum_in_vec() -> Result<()> {
        // arrange
        let types = to_named(&[ParamType::B256, ParamType::U8]);
        let variants = EnumVariants::new(types)?;
        let selector = (1, Token::U8(8), variants);
        let enum_token = Token::Enum(Box::new(selector));

        let vec_token = Token::Vector(vec![enum_token]);

        // act
        let result = ABIEncoder::default().encode(&[vec_token])?;

        // assert
        let expected = [
            0, 0, 0, 0, 0, 0, 0, 1, // vec len
            0, 0, 0, 0, 0, 0, 0, 1, 8, // enum discriminant and u8 value
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn vec_in_struct() -> Result<()> {
        // arrange
        let token = Token::Struct(vec![Token::Vector(vec![Token::U64(5)]), Token::U8(9)]);

        // act
        let result = ABIEncoder::default().encode(&[token])?;

        // assert
        let expected = [
            0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, // vec[len, u64]
            9, // u8
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn vec_in_vec() -> Result<()> {
        // arrange
        let token = Token::Vector(vec![Token::Vector(vec![Token::U8(5), Token::U8(6)])]);

        // act
        let result = ABIEncoder::default().encode(&[token])?;

        // assert
        let expected = [
            0, 0, 0, 0, 0, 0, 0, 1, // vec1 len
            0, 0, 0, 0, 0, 0, 0, 2, 5, 6, // vec2 [len, u8, u8]
        ];

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn max_depth_surpassed() {
        const MAX_DEPTH: usize = 2;
        let config = EncoderConfig {
            max_depth: MAX_DEPTH,
            ..Default::default()
        };
        let msg = "depth limit `2` reached while encoding. Try increasing it".to_string();

        [nested_struct, nested_enum, nested_tuple, nested_array]
            .iter()
            .map(|fun| fun(MAX_DEPTH + 1))
            .for_each(|token| {
                assert_encoding_failed(config, token, &msg);
            });
    }

    fn assert_encoding_failed(config: EncoderConfig, token: Token, msg: &str) {
        let encoder = ABIEncoder::new(config);

        let err = encoder.encode(&[token]);

        let Err(Error::Codec(actual_msg)) = err else {
            panic!("expected a Codec error. Got: `{err:?}`");
        };
        assert_eq!(actual_msg, msg);
    }

    fn nested_struct(depth: usize) -> Token {
        let fields = if depth == 1 {
            vec![Token::U8(255), Token::String("bloopblip".to_string())]
        } else {
            vec![nested_struct(depth - 1)]
        };

        Token::Struct(fields)
    }

    fn nested_enum(depth: usize) -> Token {
        if depth == 0 {
            return Token::U8(255);
        }

        let inner_enum = nested_enum(depth - 1);

        // Create a basic EnumSelector for the current level (the `EnumVariants` is not
        // actually accurate but it's not used for encoding)
        let selector = (
            0u64,
            inner_enum,
            EnumVariants::new(to_named(&[ParamType::U64])).unwrap(),
        );

        Token::Enum(Box::new(selector))
    }

    fn nested_array(depth: usize) -> Token {
        if depth == 1 {
            Token::Array(vec![Token::U8(255)])
        } else {
            Token::Array(vec![nested_array(depth - 1)])
        }
    }

    fn nested_tuple(depth: usize) -> Token {
        let fields = if depth == 1 {
            vec![Token::U8(255), Token::String("bloopblip".to_string())]
        } else {
            vec![nested_tuple(depth - 1)]
        };

        Token::Tuple(fields)
    }
}\n```

Configuring the encoder for contract/script calls

You can also configure the encoder used to encode the arguments of the contract method:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

The same method is available for script calls.

Decoding

Be sure to read the prerequisites to decoding.

Decoding is done via the ABIDecoder:

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        core::codec::{DecoderConfig, EncoderConfig},
        types::errors::Result,
    };

    #[test]
    fn encoding_a_type() -> Result<()> {
        //ANCHOR: encoding_example
        use fuels::{
            core::{codec::ABIEncoder, traits::Tokenizable},
            macros::Tokenizable,
        };

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let instance = MyStruct { field: 101 };
        let _encoded: Vec<u8> = ABIEncoder::default().encode(&[instance.into_token()])?;
        //ANCHOR_END: encoding_example

        Ok(())
    }
    #[test]
    fn encoding_via_macro() -> Result<()> {
        //ANCHOR: encoding_example_w_macro
        use fuels::{core::codec::calldata, macros::Tokenizable};

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }
        let _: Vec<u8> = calldata!(MyStruct { field: 101 }, MyStruct { field: 102 })?;
        //ANCHOR_END: encoding_example_w_macro

        Ok(())
    }

    #[test]
    fn decoding_example() -> Result<()> {
        // ANCHOR: decoding_example
        use fuels::{
            core::{
                codec::ABIDecoder,
                traits::{Parameterize, Tokenizable},
            },
            macros::{Parameterize, Tokenizable},
            types::Token,
        };

        #[derive(Parameterize, Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let token: Token = ABIDecoder::default().decode(&MyStruct::param_type(), bytes)?;

        let _: MyStruct = MyStruct::from_token(token)?;
        // ANCHOR_END: decoding_example

        Ok(())
    }

    #[test]
    fn decoding_example_try_into() -> Result<()> {
        // ANCHOR: decoding_example_try_into
        use fuels::macros::{Parameterize, Tokenizable, TryFrom};

        #[derive(Parameterize, Tokenizable, TryFrom)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let _: MyStruct = bytes.try_into()?;
        // ANCHOR_END: decoding_example_try_into

        Ok(())
    }

    #[test]
    fn configuring_the_decoder() -> Result<()> {
        // ANCHOR: configuring_the_decoder

        use fuels::core::codec::ABIDecoder;

        ABIDecoder::new(DecoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_decoder

        Ok(())
    }

    #[test]
    fn configuring_the_encoder() -> Result<()> {
        // ANCHOR: configuring_the_encoder
        use fuels::core::codec::ABIEncoder;

        ABIEncoder::new(EncoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_encoder

        Ok(())
    }
}\n```

First into a Token, then via the Tokenizable trait, into the desired type.

If the type came from abigen! (or uses the ::fuels::macros::TryFrom derivation) then you can also use try_into to convert bytes into a type that implements both Parameterize and Tokenizable:

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        core::codec::{DecoderConfig, EncoderConfig},
        types::errors::Result,
    };

    #[test]
    fn encoding_a_type() -> Result<()> {
        //ANCHOR: encoding_example
        use fuels::{
            core::{codec::ABIEncoder, traits::Tokenizable},
            macros::Tokenizable,
        };

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let instance = MyStruct { field: 101 };
        let _encoded: Vec<u8> = ABIEncoder::default().encode(&[instance.into_token()])?;
        //ANCHOR_END: encoding_example

        Ok(())
    }
    #[test]
    fn encoding_via_macro() -> Result<()> {
        //ANCHOR: encoding_example_w_macro
        use fuels::{core::codec::calldata, macros::Tokenizable};

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }
        let _: Vec<u8> = calldata!(MyStruct { field: 101 }, MyStruct { field: 102 })?;
        //ANCHOR_END: encoding_example_w_macro

        Ok(())
    }

    #[test]
    fn decoding_example() -> Result<()> {
        // ANCHOR: decoding_example
        use fuels::{
            core::{
                codec::ABIDecoder,
                traits::{Parameterize, Tokenizable},
            },
            macros::{Parameterize, Tokenizable},
            types::Token,
        };

        #[derive(Parameterize, Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let token: Token = ABIDecoder::default().decode(&MyStruct::param_type(), bytes)?;

        let _: MyStruct = MyStruct::from_token(token)?;
        // ANCHOR_END: decoding_example

        Ok(())
    }

    #[test]
    fn decoding_example_try_into() -> Result<()> {
        // ANCHOR: decoding_example_try_into
        use fuels::macros::{Parameterize, Tokenizable, TryFrom};

        #[derive(Parameterize, Tokenizable, TryFrom)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let _: MyStruct = bytes.try_into()?;
        // ANCHOR_END: decoding_example_try_into

        Ok(())
    }

    #[test]
    fn configuring_the_decoder() -> Result<()> {
        // ANCHOR: configuring_the_decoder

        use fuels::core::codec::ABIDecoder;

        ABIDecoder::new(DecoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_decoder

        Ok(())
    }

    #[test]
    fn configuring_the_encoder() -> Result<()> {
        // ANCHOR: configuring_the_encoder
        use fuels::core::codec::ABIEncoder;

        ABIEncoder::new(EncoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_encoder

        Ok(())
    }
}\n```

Under the hood, try_from_bytes is being called, which does what the preceding example did.

Configuring the decoder

The decoder can be configured to limit its resource expenditure:

```rust\n#[cfg(test)]
mod tests {
    use fuels::{
        core::codec::{DecoderConfig, EncoderConfig},
        types::errors::Result,
    };

    #[test]
    fn encoding_a_type() -> Result<()> {
        //ANCHOR: encoding_example
        use fuels::{
            core::{codec::ABIEncoder, traits::Tokenizable},
            macros::Tokenizable,
        };

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let instance = MyStruct { field: 101 };
        let _encoded: Vec<u8> = ABIEncoder::default().encode(&[instance.into_token()])?;
        //ANCHOR_END: encoding_example

        Ok(())
    }
    #[test]
    fn encoding_via_macro() -> Result<()> {
        //ANCHOR: encoding_example_w_macro
        use fuels::{core::codec::calldata, macros::Tokenizable};

        #[derive(Tokenizable)]
        struct MyStruct {
            field: u64,
        }
        let _: Vec<u8> = calldata!(MyStruct { field: 101 }, MyStruct { field: 102 })?;
        //ANCHOR_END: encoding_example_w_macro

        Ok(())
    }

    #[test]
    fn decoding_example() -> Result<()> {
        // ANCHOR: decoding_example
        use fuels::{
            core::{
                codec::ABIDecoder,
                traits::{Parameterize, Tokenizable},
            },
            macros::{Parameterize, Tokenizable},
            types::Token,
        };

        #[derive(Parameterize, Tokenizable)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let token: Token = ABIDecoder::default().decode(&MyStruct::param_type(), bytes)?;

        let _: MyStruct = MyStruct::from_token(token)?;
        // ANCHOR_END: decoding_example

        Ok(())
    }

    #[test]
    fn decoding_example_try_into() -> Result<()> {
        // ANCHOR: decoding_example_try_into
        use fuels::macros::{Parameterize, Tokenizable, TryFrom};

        #[derive(Parameterize, Tokenizable, TryFrom)]
        struct MyStruct {
            field: u64,
        }

        let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];

        let _: MyStruct = bytes.try_into()?;
        // ANCHOR_END: decoding_example_try_into

        Ok(())
    }

    #[test]
    fn configuring_the_decoder() -> Result<()> {
        // ANCHOR: configuring_the_decoder

        use fuels::core::codec::ABIDecoder;

        ABIDecoder::new(DecoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_decoder

        Ok(())
    }

    #[test]
    fn configuring_the_encoder() -> Result<()> {
        // ANCHOR: configuring_the_encoder
        use fuels::core::codec::ABIEncoder;

        ABIEncoder::new(EncoderConfig {
            max_depth: 5,
            max_tokens: 100,
        });
        // ANCHOR_END: configuring_the_encoder

        Ok(())
    }
}\n```

For an explanation of each configuration value visit the DecoderConfig.

The default values for the DecoderConfig are:

```rust\nmod bounded_decoder;
mod decode_as_debug_str;

use std::io::Read;

use crate::{
    codec::abi_decoder::{
        bounded_decoder::BoundedDecoder, decode_as_debug_str::decode_as_debug_str,
    },
    types::{errors::Result, param_types::ParamType, Token},
};

#[derive(Debug, Clone, Copy)]
pub struct DecoderConfig {
    /// Entering a struct, array, tuple, enum or vector increases the depth. Decoding will fail if
    /// the current depth becomes greater than `max_depth` configured here.
    pub max_depth: usize,
    /// Every decoded Token will increase the token count. Decoding will fail if the current
    /// token count becomes greater than `max_tokens` configured here.
    pub max_tokens: usize,
}

// ANCHOR: default_decoder_config
impl Default for DecoderConfig {
    fn default() -> Self {
        Self {
            max_depth: 45,
            max_tokens: 10_000,
        }
    }
}
// ANCHOR_END: default_decoder_config

#[derive(Default)]
pub struct ABIDecoder {
    pub config: DecoderConfig,
}

impl ABIDecoder {
    pub fn new(config: DecoderConfig) -> Self {
        Self { config }
    }

    /// Decodes `bytes` following the schema described in `param_type` into its respective `Token`.
    ///
    /// # Arguments
    ///
    /// * `param_type`: The `ParamType` of the type we expect is encoded
    ///                  inside `bytes`.
    /// * `bytes`:       The bytes to be used in the decoding process.
    /// # Examples
    ///
    /// ```
    /// use fuels_core::codec::ABIDecoder;
    /// use fuels_core::traits::Tokenizable;
    /// use fuels_core::types::param_types::ParamType;
    ///
    /// let decoder = ABIDecoder::default();
    ///
    /// let token = decoder.decode(&ParamType::U64,  [0, 0, 0, 0, 0, 0, 0, 7].as_slice()).unwrap();
    ///
    /// assert_eq!(u64::from_token(token).unwrap(), 7u64);
    /// ```
    pub fn decode(&self, param_type: &ParamType, mut bytes: impl Read) -> Result<Token> {
        BoundedDecoder::new(self.config).decode(param_type, &mut bytes)
    }

    /// Same as `decode` but decodes multiple `ParamType`s in one go.
    /// # Examples
    /// ```
    /// use fuels_core::codec::ABIDecoder;
    /// use fuels_core::types::param_types::ParamType;
    /// use fuels_core::types::Token;
    ///
    /// let decoder = ABIDecoder::default();
    /// let data = [7, 8];
    ///
    /// let tokens = decoder.decode_multiple(&[ParamType::U8, ParamType::U8], data.as_slice()).unwrap();
    ///
    /// assert_eq!(tokens, vec![Token::U8(7), Token::U8(8)]);
    /// ```
    pub fn decode_multiple(
        &self,
        param_types: &[ParamType],
        mut bytes: impl Read,
    ) -> Result<Vec<Token>> {
        BoundedDecoder::new(self.config).decode_multiple(param_types, &mut bytes)
    }

    /// Decodes `bytes` following the schema described in `param_type` into its respective debug
    /// string.
    ///
    /// # Arguments
    ///
    /// * `param_type`: The `ParamType` of the type we expect is encoded
    ///                  inside `bytes`.
    /// * `bytes`:       The bytes to be used in the decoding process.
    /// # Examples
    ///
    /// ```
    /// use fuels_core::codec::ABIDecoder;
    /// use fuels_core::types::param_types::ParamType;
    ///
    /// let decoder = ABIDecoder::default();
    ///
    /// let debug_string = decoder.decode_as_debug_str(&ParamType::U64,  [0, 0, 0, 0, 0, 0, 0, 7].as_slice()).unwrap();
    /// let expected_value = 7u64;
    ///
    /// assert_eq!(debug_string, format!("{expected_value}"));
    /// ```
    pub fn decode_as_debug_str(
        &self,
        param_type: &ParamType,
        mut bytes: impl Read,
    ) -> Result<String> {
        let token = BoundedDecoder::new(self.config).decode(param_type, &mut bytes)?;
        decode_as_debug_str(param_type, &token)
    }

    pub fn decode_multiple_as_debug_str(
        &self,
        param_types: &[ParamType],
        mut bytes: impl Read,
    ) -> Result<Vec<String>> {
        let token = BoundedDecoder::new(self.config).decode_multiple(param_types, &mut bytes)?;
        token
            .into_iter()
            .zip(param_types)
            .map(|(token, param_type)| decode_as_debug_str(param_type, &token))
            .collect()
    }
}

#[cfg(test)]
mod tests {
    use std::vec;

    use ParamType::*;

    use super::*;
    use crate::{
        constants::WORD_SIZE,
        to_named,
        traits::Parameterize,
        types::{errors::Error, param_types::EnumVariants, StaticStringToken, U256},
    };

    #[test]
    fn decode_multiple_uint() -> Result<()> {
        let types = vec![
            ParamType::U8,
            ParamType::U16,
            ParamType::U32,
            ParamType::U64,
            ParamType::U128,
            ParamType::U256,
        ];

        let data = [
            255, // u8
            255, 255, // u16
            255, 255, 255, 255, // u32
            255, 255, 255, 255, 255, 255, 255, 255, // u64
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, // u128
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // u256
        ];

        let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?;

        let expected = vec![
            Token::U8(u8::MAX),
            Token::U16(u16::MAX),
            Token::U32(u32::MAX),
            Token::U64(u64::MAX),
            Token::U128(u128::MAX),
            Token::U256(U256::MAX),
        ];
        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn decode_bool() -> Result<()> {
        let types = vec![ParamType::Bool, ParamType::Bool];
        let data = [1, 0];

        let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?;

        let expected = vec![Token::Bool(true), Token::Bool(false)];

        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn decode_b256() -> Result<()> {
        let data = [
            213, 87, 156, 70, 223, 204, 127, 24, 32, 112, 19, 230, 91, 68, 228, 203, 78, 44, 34,
            152, 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11,
        ];

        let decoded = ABIDecoder::default().decode(&ParamType::B256, data.as_slice())?;

        assert_eq!(decoded, Token::B256(data));

        Ok(())
    }

    #[test]
    fn decode_string_array() -> Result<()> {
        let types = vec![ParamType::StringArray(23), ParamType::StringArray(5)];
        let data = [
            84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110,
            116, 101, 110, 99, 101, //This is a full sentence
            72, 101, 108, 108, 111, // Hello
        ];

        let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?;

        let expected = vec![
            Token::StringArray(StaticStringToken::new(
                "This is a full sentence".into(),
                Some(23),
            )),
            Token::StringArray(StaticStringToken::new("Hello".into(), Some(5))),
        ];

        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn decode_string_slice() -> Result<()> {
        let data = [
            0, 0, 0, 0, 0, 0, 0, 23, // [length]
            84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110,
            116, 101, 110, 99, 101, //This is a full sentence
        ];

        let decoded = ABIDecoder::default().decode(&ParamType::StringSlice, data.as_slice())?;

        let expected = Token::StringSlice(StaticStringToken::new(
            "This is a full sentence".into(),
            None,
        ));

        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn decode_string() -> Result<()> {
        let data = [
            0, 0, 0, 0, 0, 0, 0, 23, // [length]
            84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110,
            116, 101, 110, 99, 101, //This is a full sentence
        ];

        let decoded = ABIDecoder::default().decode(&ParamType::String, data.as_slice())?;

        let expected = Token::String("This is a full sentence".to_string());

        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn decode_tuple() -> Result<()> {
        let param_type = ParamType::Tuple(vec![ParamType::U32, ParamType::Bool]);
        let data = [
            0, 0, 0, 255, //u32
            1,   //bool
        ];

        let result = ABIDecoder::default().decode(&param_type, data.as_slice())?;

        let expected = Token::Tuple(vec![Token::U32(255), Token::Bool(true)]);

        assert_eq!(result, expected);

        Ok(())
    }

    #[test]
    fn decode_array() -> Result<()> {
        let types = vec![ParamType::Array(Box::new(ParamType::U8), 2)];
        let data = [255, 42];

        let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?;

        let expected = vec![Token::Array(vec![Token::U8(255), Token::U8(42)])];
        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn decode_struct() -> Result<()> {
        // struct MyStruct {
        //     foo: u8,
        //     bar: bool,
        // }

        let data = [1, 1];

        let param_type = ParamType::Struct {
            name: "".to_string(),
            fields: to_named(&[ParamType::U8, ParamType::Bool]),
            generics: vec![],
        };

        let decoded = ABIDecoder::default().decode(&param_type, data.as_slice())?;

        let expected = Token::Struct(vec![Token::U8(1), Token::Bool(true)]);

        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn decode_bytes() -> Result<()> {
        let data = [0, 0, 0, 0, 0, 0, 0, 7, 255, 0, 1, 2, 3, 4, 5];

        let decoded = ABIDecoder::default().decode(&ParamType::Bytes, data.as_slice())?;

        let expected = Token::Bytes([255, 0, 1, 2, 3, 4, 5].to_vec());

        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn decode_raw_slice() -> Result<()> {
        let data = [0, 0, 0, 0, 0, 0, 0, 7, 255, 0, 1, 2, 3, 4, 5];

        let decoded = ABIDecoder::default().decode(&ParamType::RawSlice, data.as_slice())?;

        let expected = Token::RawSlice([255, 0, 1, 2, 3, 4, 5].to_vec());

        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn decode_enum() -> Result<()> {
        // enum MyEnum {
        //     x: u32,
        //     y: bool,
        // }

        let types = to_named(&[ParamType::U32, ParamType::Bool]);
        let inner_enum_types = EnumVariants::new(types)?;
        let types = vec![ParamType::Enum {
            name: "".to_string(),
            enum_variants: inner_enum_types.clone(),
            generics: vec![],
        }];

        let data = [
            0, 0, 0, 0, 0, 0, 0, 0, // discriminant
            0, 0, 0, 42, // u32
        ];

        let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?;

        let expected = vec![Token::Enum(Box::new((0, Token::U32(42), inner_enum_types)))];
        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn decode_nested_struct() -> Result<()> {
        // struct Foo {
        //     x: u16,
        //     y: Bar,
        // }
        //
        // struct Bar {
        //     a: bool,
        //     b: u8[2],
        // }

        let fields = to_named(&[
            ParamType::U16,
            ParamType::Struct {
                name: "".to_string(),
                fields: to_named(&[
                    ParamType::Bool,
                    ParamType::Array(Box::new(ParamType::U8), 2),
                ]),
                generics: vec![],
            },
        ]);
        let nested_struct = ParamType::Struct {
            name: "".to_string(),
            fields,
            generics: vec![],
        };

        let data = [0, 10, 1, 1, 2];

        let decoded = ABIDecoder::default().decode(&nested_struct, data.as_slice())?;

        let my_nested_struct = vec![
            Token::U16(10),
            Token::Struct(vec![
                Token::Bool(true),
                Token::Array(vec![Token::U8(1), Token::U8(2)]),
            ]),
        ];

        assert_eq!(decoded, Token::Struct(my_nested_struct));

        Ok(())
    }

    #[test]
    fn decode_comprehensive() -> Result<()> {
        // struct Foo {
        //     x: u16,
        //     y: Bar,
        // }
        //
        // struct Bar {
        //     a: bool,
        //     b: u8[2],
        // }

        // fn: long_function(Foo,u8[2],b256,str[3],str)

        // Parameters
        let fields = to_named(&[
            ParamType::U16,
            ParamType::Struct {
                name: "".to_string(),
                fields: to_named(&[
                    ParamType::Bool,
                    ParamType::Array(Box::new(ParamType::U8), 2),
                ]),
                generics: vec![],
            },
        ]);
        let nested_struct = ParamType::Struct {
            name: "".to_string(),
            fields,
            generics: vec![],
        };

        let u8_arr = ParamType::Array(Box::new(ParamType::U8), 2);
        let b256 = ParamType::B256;

        let types = [nested_struct, u8_arr, b256];

        let bytes = [
            0, 10, // u16
            1,  // bool
            1, 2, // array[u8;2]
            1, 2, // array[u8;2]
            213, 87, 156, 70, 223, 204, 127, 24, 32, 112, 19, 230, 91, 68, 228, 203, 78, 44, 34,
            152, 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11, // b256
        ];

        let decoded = ABIDecoder::default().decode_multiple(&types, bytes.as_slice())?;

        // Expected tokens
        let foo = Token::Struct(vec![
            Token::U16(10),
            Token::Struct(vec![
                Token::Bool(true),
                Token::Array(vec![Token::U8(1), Token::U8(2)]),
            ]),
        ]);

        let u8_arr = Token::Array(vec![Token::U8(1), Token::U8(2)]);

        let b256 = Token::B256([
            213, 87, 156, 70, 223, 204, 127, 24, 32, 112, 19, 230, 91, 68, 228, 203, 78, 44, 34,
            152, 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11,
        ]);

        let expected: Vec<Token> = vec![foo, u8_arr, b256];

        assert_eq!(decoded, expected);

        Ok(())
    }

    #[test]
    fn enums_with_all_unit_variants_are_decoded_from_one_word() -> Result<()> {
        let data = [0, 0, 0, 0, 0, 0, 0, 1];
        let types = to_named(&[ParamType::Unit, ParamType::Unit]);
        let enum_variants = EnumVariants::new(types)?;
        let enum_w_only_units = ParamType::Enum {
            name: "".to_string(),
            enum_variants: enum_variants.clone(),
            generics: vec![],
        };

        let result = ABIDecoder::default().decode(&enum_w_only_units, data.as_slice())?;

        let expected_enum = Token::Enum(Box::new((1, Token::Unit, enum_variants)));
        assert_eq!(result, expected_enum);

        Ok(())
    }

    #[test]
    fn out_of_bounds_discriminant_is_detected() -> Result<()> {
        let data = [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];
        let types = to_named(&[ParamType::U64]);
        let enum_variants = EnumVariants::new(types)?;
        let enum_type = ParamType::Enum {
            name: "".to_string(),
            enum_variants,
            generics: vec![],
        };

        let result = ABIDecoder::default().decode(&enum_type, data.as_slice());

        let error = result.expect_err("should have resulted in an error");

        let expected_msg = "discriminant `1` doesn't point to any variant: ";
        assert!(matches!(error, Error::Other(str) if str.starts_with(expected_msg)));

        Ok(())
    }

    #[test]
    pub fn division_by_zero() {
        let param_type = Vec::<[u16; 0]>::param_type();
        let result = ABIDecoder::default().decode(&param_type, [].as_slice());
        assert!(matches!(result, Err(Error::IO(_))));
    }

    #[test]
    pub fn multiply_overflow_enum() {
        let result = ABIDecoder::default().decode(
            &Enum {
                name: "".to_string(),
                enum_variants: EnumVariants::new(to_named(&[
                    Array(Box::new(Array(Box::new(RawSlice), 8)), usize::MAX),
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                ]))
                .unwrap(),
                generics: vec![U16],
            },
            [].as_slice(),
        );

        assert!(matches!(result, Err(Error::IO(_))));
    }

    #[test]
    pub fn multiply_overflow_arith() {
        let mut param_type: ParamType = U16;
        for _ in 0..50 {
            param_type = Array(Box::new(param_type), 8);
        }
        let result = ABIDecoder::default().decode(
            &Enum {
                name: "".to_string(),
                enum_variants: EnumVariants::new(to_named(&[param_type])).unwrap(),
                generics: vec![U16],
            },
            [].as_slice(),
        );
        assert!(matches!(result, Err(Error::IO(_))));
    }

    #[test]
    pub fn capacity_overflow() {
        let result = ABIDecoder::default().decode(
            &Array(Box::new(Array(Box::new(Tuple(vec![])), usize::MAX)), 1),
            [].as_slice(),
        );
        assert!(matches!(result, Err(Error::Codec(_))));
    }

    #[test]
    pub fn stack_overflow() {
        let mut param_type: ParamType = U16;
        for _ in 0..13500 {
            param_type = Vector(Box::new(param_type));
        }
        let result = ABIDecoder::default().decode(&param_type, [].as_slice());
        assert!(matches!(result, Err(Error::IO(_))));
    }

    #[test]
    pub fn capacity_malloc() {
        let param_type = Array(Box::new(U8), usize::MAX);
        let result = ABIDecoder::default().decode(&param_type, [].as_slice());
        assert!(matches!(result, Err(Error::IO(_))));
    }

    #[test]
    fn max_depth_surpassed() {
        const MAX_DEPTH: usize = 2;
        let config = DecoderConfig {
            max_depth: MAX_DEPTH,
            ..Default::default()
        };
        let msg = format!("depth limit `{MAX_DEPTH}` reached while decoding. Try increasing it");
        // for each nested enum so that it may read the discriminant
        let data = [0; MAX_DEPTH * WORD_SIZE];

        [nested_struct, nested_enum, nested_tuple, nested_array]
            .iter()
            .map(|fun| fun(MAX_DEPTH + 1))
            .for_each(|param_type| {
                assert_decoding_failed_w_data(config, &param_type, &msg, data.as_slice());
            })
    }

    #[test]
    fn depth_is_not_reached() {
        const MAX_DEPTH: usize = 3;
        const ACTUAL_DEPTH: usize = MAX_DEPTH - 1;

        // enough data to decode 2*ACTUAL_DEPTH enums (discriminant + u8 = 2*WORD_SIZE)
        let data = [0; 2 * ACTUAL_DEPTH * (WORD_SIZE * 2)];
        let config = DecoderConfig {
            max_depth: MAX_DEPTH,
            ..Default::default()
        };

        [nested_struct, nested_enum, nested_tuple, nested_array]
            .into_iter()
            .map(|fun| fun(ACTUAL_DEPTH))
            .map(|param_type| {
                // Wrapping everything in a structure so that we may check whether the depth is
                // decremented after finishing every struct field.
                ParamType::Struct {
                    name: "".to_string(),
                    fields: to_named(&[param_type.clone(), param_type]),
                    generics: vec![],
                }
            })
            .for_each(|param_type| {
                ABIDecoder::new(config)
                    .decode(&param_type, data.as_slice())
                    .unwrap();
            })
    }

    #[test]
    fn too_many_tokens() {
        let config = DecoderConfig {
            max_tokens: 3,
            ..Default::default()
        };
        {
            let data = [0; 3 * WORD_SIZE];
            let inner_param_types = vec![ParamType::U64; 3];
            for param_type in [
                ParamType::Struct {
                    name: "".to_string(),
                    fields: to_named(&inner_param_types),
                    generics: vec![],
                },
                ParamType::Tuple(inner_param_types.clone()),
                ParamType::Array(Box::new(ParamType::U64), 3),
            ] {
                assert_decoding_failed_w_data(
                    config,
                    &param_type,
                    "token limit `3` reached while decoding. Try increasing it",
                    &data,
                );
            }
        }
        {
            let data = [0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3];

            assert_decoding_failed_w_data(
                config,
                &ParamType::Vector(Box::new(ParamType::U8)),
                "token limit `3` reached while decoding. Try increasing it",
                &data,
            );
        }
    }

    #[test]
    fn token_count_is_being_reset_between_decodings() {
        // given
        let config = DecoderConfig {
            max_tokens: 3,
            ..Default::default()
        };

        let param_type = ParamType::Array(Box::new(ParamType::StringArray(0)), 2);

        let decoder = ABIDecoder::new(config);
        decoder.decode(&param_type, [].as_slice()).unwrap();

        // when
        let result = decoder.decode(&param_type, [].as_slice());

        // then
        result.expect("element count to be reset");
    }

    fn assert_decoding_failed_w_data(
        config: DecoderConfig,
        param_type: &ParamType,
        msg: &str,
        data: &[u8],
    ) {
        let decoder = ABIDecoder::new(config);

        let err = decoder.decode(param_type, data);

        let Err(Error::Codec(actual_msg)) = err else {
            panic!("expected a `Codec` error. Got: `{err:?}`");
        };

        assert_eq!(actual_msg, msg);
    }

    fn nested_struct(depth: usize) -> ParamType {
        let fields = if depth == 1 {
            vec![]
        } else {
            to_named(&[nested_struct(depth - 1)])
        };

        ParamType::Struct {
            name: "".to_string(),
            fields,
            generics: vec![],
        }
    }

    fn nested_enum(depth: usize) -> ParamType {
        let fields = if depth == 1 {
            to_named(&[ParamType::U8])
        } else {
            to_named(&[nested_enum(depth - 1)])
        };

        ParamType::Enum {
            name: "".to_string(),
            enum_variants: EnumVariants::new(fields).unwrap(),
            generics: vec![],
        }
    }

    fn nested_array(depth: usize) -> ParamType {
        let field = if depth == 1 {
            ParamType::U8
        } else {
            nested_array(depth - 1)
        };

        ParamType::Array(Box::new(field), 1)
    }

    fn nested_tuple(depth: usize) -> ParamType {
        let fields = if depth == 1 {
            vec![ParamType::U8]
        } else {
            vec![nested_tuple(depth - 1)]
        };

        ParamType::Tuple(fields)
    }
}\n```

Configuring the decoder for contract/script calls

You can also configure the decoder used to decode the return value of the contract method:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

The same method is available for script calls.

API Reference

For a more in-depth look at the APIs provided by the Fuel Rust SDK, head over to the official documentation. In the actual Rust docs, you can see the most up-to-date information about the API, which is synced with the code as it changes.

fuels-rs Testing

note This section is still a work in progress.

Testing Basics

If you're new to Rust, you'll want to review these important tools to help you build tests.

The assert! macro

You can use the assert! macro to assert certain conditions in your test. This macro invokes panic!() and fails the test if the expression inside evaluates to false.

assert!(value == 5);

The assert_eq! macro

The assert_eq! macro works a lot like the assert macro, however instead it accepts two values, and panics if those values are not equal.

assert_eq!(balance, 100);

The assert_ne! macro

The assert_ne! macro works just like the assert_eq! macro, but it will panic if the two values are equal.

assert_ne!(address, 0);

The println! macro

You can use the println! macro to print values to the console.

println!("WALLET 1 ADDRESS {}", wallet_1.address());
println!("WALLET 1 ADDRESS {:?}", wallet_1.address());

Using {} will print the value, and using {:?} will print the value plus its type.

Using {:?} will also allow you to print values that do not have the Display trait implemented but do have the Debug trait. Alternatively you can use the dbg! macro to print these types of variables.

println!("WALLET 1 PROVIDER {:?}", wallet_1.provider().unwrap());
dbg!("WALLET 1 PROVIDER {}", wallet_1.provider().unwrap());

To print more complex types that don't have it already, you can implement your own formatted display method with the fmt module from the Rust standard library.

use std::fmt;

struct Point {
    x: u64,
    y: u64,
}

// add print functionality with the fmt module 
impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "value of x: {}, value of y: {}", self.x, self.y)
    }
}

let p = Point {x: 1, y: 2};
println!("POINT: {}", p);

Run Commands

You can run your tests to see if they pass or fail with

cargo test

Outputs will be hidden if the test passes. If you want to see outputs printed from your tests regardless of whether they pass or fail, use the nocapture flag.

cargo test -- --nocapture

The setup_program_test! macro

When deploying contracts with the abigen! macro, as shown in the previous sections, the user can:

  • change the default configuration parameters
  • launch several providers
  • create multiple wallets
  • create specific assets, etc.

However, it is often the case that we want to quickly set up a test with default values and work directly with contract or script instances. The setup_program_test! can do exactly that.


Used to reduce boilerplate in integration tests. Accepts input in the form of COMMAND(ARG...)...

COMMAND is either Wallets, Abigen, LoadScript or Deploy.

ARG is either a:

  • name-value (e.g. name="MyContract"), or,
  • a literal (e.g. "some_str_literal", true, 5, ...)
  • a sub-command (e.g. Abigen(Contract(name="MyContract", project="some_project")))

Available COMMANDs:

Options

Example: Options(profile="debug")

Description: Sets options from ARGs to be used by other COMMANDs.

Available options:

  • profile: sets the cargo build profile. Variants: "release" (default), "debug"

Cardinality: 0 or 1.

Wallets

Example: Wallets("a_wallet", "another_wallet"...)

Description: Launches a local provider and generates wallets with names taken from the provided ARGs.

Cardinality: 0 or 1.

Abigen

Example:

Abigen(
    Contract(
        name = "MyContract",
        project = "some_folder"
    ),
    Script(
        name = "MyScript",
        project = "some_folder"
    ),
    Predicate(
        name = "MyPredicate",
        project = "some_folder"
    ),
)

Description: Generates the program bindings under the name name. project should point to root of the forc project. The project must be compiled in release mode (--release flag) for Abigen command to work.

Cardinality: 0 or N.

Deploy

Example: Deploy(name="instance_name", contract="MyContract", wallet="a_wallet")

Description: Deploys the contract (with salt) using wallet. Will create a contract instance accessible via name. Due to salt usage, the same contract can be deployed multiple times. Requires that an Abigen command be present with name equal to contract. wallet can either be one of the wallets in the Wallets COMMAND or the name of a wallet you've previously generated yourself.

Cardinality: 0 or N.

LoadScript

Example: LoadScript(name = "script_instance", script = "MyScript", wallet = "wallet")

Description: Creates a script instance of script under name using wallet.

Cardinality: 0 or N.


The setup code that you have seen in previous sections gets reduced to:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

Note The same contract can be deployed several times as the macro deploys the contracts with salt. You can also deploy different contracts to the same provider by referencing the same wallet in the Deploy command.

```rust\nuse std::time::Duration;

use fuel_tx::{
    consensus_parameters::{ConsensusParametersV1, FeeParametersV1},
    ConsensusParameters, FeeParameters, Output,
};
use fuels::{
    core::codec::{calldata, encode_fn_selector, DecoderConfig, EncoderConfig},
    prelude::*,
    programs::DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
    tx::ContractParameters,
    types::{errors::transaction::Reason, input::Input, Bits256, Identity},
};
use tokio::time::Instant;

#[tokio::test]
async fn test_multiple_args() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Make sure we can call the contract with multiple arguments
    let contract_methods = contract_instance.methods();
    let response = contract_methods.get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    let t = MyType { x: 5, y: 6 };
    let response = contract_methods.get_alt(t.clone()).call().await?;
    assert_eq!(response.value, t);

    let response = contract_methods.get_single(5).call().await?;
    assert_eq!(response.value, 5);
    Ok(())
}

#[tokio::test]
async fn test_contract_calling_contract() -> Result<()> {
    // Tests a contract call that calls another contract (FooCaller calls FooContract underneath)
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "lib_contract_instance2",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();
    let lib_contract_id2 = lib_contract_instance2.contract_id();

    // Call the contract directly. It increments the given value.
    let response = lib_contract_instance.methods().increment(42).call().await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance
        .methods()
        .increment_from_contracts(lib_contract_id, lib_contract_id2, 42)
        // Note that the two lib_contract_instances have different types
        .with_contracts(&[&lib_contract_instance, &lib_contract_instance2])
        .call()
        .await?;

    assert_eq!(86, response.value);

    // ANCHOR: external_contract
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    assert_eq!(43, response.value);

    // ANCHOR: external_contract_ids
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contract_ids(&[lib_contract_id.clone()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    assert_eq!(43, response.value);
    Ok(())
}

#[tokio::test]
async fn test_reverting_transaction() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertContract",
            project = "e2e/sway/contracts/revert_transaction_error"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .make_transaction_fail(true)
        .call()
        .await;

    assert!(matches!(
        response,
        Err(Error::Transaction(Reason::Reverted { revert_id, .. })) if revert_id == 128
    ));

    Ok(())
}

#[tokio::test]
async fn test_multiple_read_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MultiReadContract",
            project = "e2e/sway/contracts/multiple_read_calls"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MultiReadContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    contract_methods.store(42).call().await?;

    // Use "simulate" because the methods don't actually
    // run a transaction, but just a dry-run
    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);

    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_multi_call_beginner() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let (val_1, val_2): (u64, u64) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_pro() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let my_type_1 = MyType { x: 1, y: 2 };
    let my_type_2 = MyType { x: 3, y: 4 };

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(5);
    let call_handler_2 = contract_methods.get_single(6);
    let call_handler_3 = contract_methods.get_alt(my_type_1.clone());
    let call_handler_4 = contract_methods.get_alt(my_type_2.clone());
    let call_handler_5 = contract_methods.get_array([7; 2]);
    let call_handler_6 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2)
        .add_call(call_handler_3)
        .add_call(call_handler_4)
        .add_call(call_handler_5)
        .add_call(call_handler_6);

    let (val_1, val_2, type_1, type_2, array_1, array_2): (
        u64,
        u64,
        MyType,
        MyType,
        [u64; 2],
        [u64; 2],
    ) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 5);
    assert_eq!(val_2, 6);
    assert_eq!(type_1, my_type_1);
    assert_eq!(type_2, my_type_2);
    assert_eq!(array_1, [7; 2]);
    assert_eq!(array_2, [42; 2]);

    Ok(())
}

#[tokio::test]
async fn test_contract_call_fee_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let gas_limit = 800;
    let tolerance = Some(0.2);
    let block_horizon = Some(1);
    let expected_gas_used = 960;
    let expected_metered_bytes_size = 824;

    let estimated_transaction_cost = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit))
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?;

    assert_eq!(estimated_transaction_cost.gas_used, expected_gas_used);
    assert_eq!(
        estimated_transaction_cost.metered_bytes_size,
        expected_metered_bytes_size
    );

    Ok(())
}

#[tokio::test]
async fn contract_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let estimated_gas_used = contract_methods
        .initialize_counter(42)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn mult_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.initialize_counter(42);
    let call_handler_2 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let tolerance = Some(0.0);
    let block_horizon = Some(1);
    let estimated_gas_used = multi_call_handler
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = multi_call_handler.call::<(u64, [u64; 2])>().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn contract_method_call_respects_maturity() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BlockHeightContract",
            project = "e2e/sway/contracts/transaction_block_height"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BlockHeightContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_w_maturity = |maturity| {
        contract_instance
            .methods()
            .calling_this_will_produce_a_block()
            .with_tx_policies(TxPolicies::default().with_maturity(maturity))
    };

    call_w_maturity(1).call().await.expect(
        "should have passed since we're calling with a maturity \
         that is less or equal to the current block height",
    );

    call_w_maturity(3).call().await.expect_err(
        "should have failed since we're calling with a maturity \
         that is greater than the current block height",
    );

    Ok(())
}

#[tokio::test]
async fn test_auth_msg_sender_from_sdk() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "AuthContract",
            project = "e2e/sway/contracts/auth_testing_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "AuthContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Contract returns true if `msg_sender()` matches `wallet.address()`.
    let response = contract_instance
        .methods()
        .check_msg_sender(wallet.address())
        .call()
        .await?;

    assert!(response.value);
    Ok(())
}

#[tokio::test]
async fn test_large_return_data() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/large_return_data"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let res = contract_methods.get_id().call().await?;

    assert_eq!(
        res.value.0,
        [
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ]
    );

    // One word-sized string
    let res = contract_methods.get_small_string().call().await?;
    assert_eq!(res.value, "gggggggg");

    // Two word-sized string
    let res = contract_methods.get_large_string().call().await?;
    assert_eq!(res.value, "ggggggggg");

    // Large struct will be bigger than a `WORD`.
    let res = contract_methods.get_large_struct().call().await?;
    assert_eq!(res.value.foo, 12);
    assert_eq!(res.value.bar, 42);

    // Array will be returned in `ReturnData`.
    let res = contract_methods.get_large_array().call().await?;
    assert_eq!(res.value, [1, 2]);

    let res = contract_methods.get_contract_id().call().await?;

    // First `value` is from `CallResponse`.
    // Second `value` is from the `ContractId` type.
    assert_eq!(
        res.value,
        ContractId::from([
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ])
    );
    Ok(())
}

#[tokio::test]
async fn can_handle_function_called_new() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().new().call().await?.value;

    assert_eq!(response, 12345);
    Ok(())
}

#[tokio::test]
async fn test_contract_setup_macro_deploy_with_salt() -> Result<()> {
    // ANCHOR: contract_setup_macro_multi
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_caller_id = contract_caller_instance.contract_id();

    let contract_caller_id2 = contract_caller_instance2.contract_id();

    // Because we deploy with salt, we can deploy the same contract multiple times
    assert_ne!(contract_caller_id, contract_caller_id2);

    // The first contract can be called because they were deployed on the same provider
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance2
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);
    // ANCHOR_END: contract_setup_macro_multi

    Ok(())
}

#[tokio::test]
async fn test_wallet_getter() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(contract_instance.account().address(), wallet.address());
    //`contract_id()` is tested in
    // async fn test_contract_calling_contract() -> Result<()> {
    Ok(())
}

#[tokio::test]
async fn test_connect_wallet() -> Result<()> {
    // ANCHOR: contract_setup_macro_manual_wallet
    let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));

    let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
    let wallet = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    // ANCHOR_END: contract_setup_macro_manual_wallet

    // pay for call with wallet
    let tx_policies = TxPolicies::default()
        .with_tip(100)
        .with_script_gas_limit(1_000_000);

    contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm that funds have been deducted
    let wallet_balance = wallet.get_asset_balance(&Default::default()).await?;
    assert!(DEFAULT_COIN_AMOUNT > wallet_balance);

    // pay for call with wallet_2
    contract_instance
        .with_account(wallet_2.clone())
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm there are no changes to wallet, wallet_2 has been charged
    let wallet_balance_second_call = wallet.get_asset_balance(&Default::default()).await?;
    let wallet_2_balance = wallet_2.get_asset_balance(&Default::default()).await?;
    assert_eq!(wallet_balance_second_call, wallet_balance);
    assert!(DEFAULT_COIN_AMOUNT > wallet_2_balance);

    Ok(())
}

async fn setup_output_variable_estimation_test() -> Result<(
    Vec<WalletUnlocked>,
    [Identity; 3],
    AssetId,
    Bech32ContractId,
)> {
    let wallet_config = WalletsConfig::new(Some(3), None, None);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;

    let contract_id = Contract::load_from(
        "sway/contracts/token_ops/out/release/token_ops.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallets[0], TxPolicies::default())
    .await?;

    let mint_asset_id = contract_id.asset_id(&Bits256::zeroed());
    let addresses = wallets
        .iter()
        .map(|wallet| wallet.address().into())
        .collect::<Vec<_>>()
        .try_into()
        .unwrap();

    Ok((wallets, addresses, mint_asset_id, contract_id))
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());
    let contract_methods = contract_instance.methods();
    let amount = 1000;

    {
        // Should fail due to lack of output variables
        let response = contract_methods
            .mint_to_addresses(amount, addresses)
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    {
        // Should add 3 output variables automatically
        let _ = contract_methods
            .mint_to_addresses(amount, addresses)
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .call()
            .await?;

        for wallet in wallets.iter() {
            let balance = wallet.get_asset_balance(&mint_asset_id).await?;
            assert_eq!(balance, amount);
        }
    }

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallets[0].clone());
    let contract_methods = contract_instance.methods();
    const NUM_OF_CALLS: u64 = 3;
    let amount = 1000;
    let total_amount = amount * NUM_OF_CALLS;

    let mut multi_call_handler = CallHandler::new_multi_call(wallets[0].clone());
    for _ in 0..NUM_OF_CALLS {
        let call_handler = contract_methods.mint_to_addresses(amount, addresses);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    wallets[0]
        .force_transfer_to_contract(
            &contract_id,
            total_amount,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await
        .unwrap();

    let base_layer_address = Bits256([1u8; 32]);
    let call_handler = contract_methods.send_message(base_layer_address, amount);
    multi_call_handler = multi_call_handler.add_call(call_handler);

    let _ = multi_call_handler
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call::<((), (), ())>()
        .await?;

    for wallet in wallets.iter() {
        let balance = wallet.get_asset_balance(&mint_asset_id).await?;
        assert_eq!(balance, 3 * amount);
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_instance_get_balances() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let (coins, asset_ids) = setup_multiple_assets_coins(wallet.address(), 2, 4, 8);

    let random_asset_id = &asset_ids[1];
    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_id = contract_instance.contract_id();

    // Check the current balance of the contract with id 'contract_id'
    let contract_balances = contract_instance.get_balances().await?;
    assert!(contract_balances.is_empty());

    // Transfer an amount to the contract
    let amount = 8;
    wallet
        .force_transfer_to_contract(contract_id, amount, *random_asset_id, TxPolicies::default())
        .await?;

    // Check that the contract now has 1 coin
    let contract_balances = contract_instance.get_balances().await?;
    assert_eq!(contract_balances.len(), 1);

    let random_asset_balance = contract_balances.get(random_asset_id).unwrap();
    assert_eq!(*random_asset_balance, amount);

    Ok(())
}

#[tokio::test]
async fn contract_call_futures_implement_send() -> Result<()> {
    use std::future::Future;

    fn tokio_spawn_imitation<T>(_: T)
    where
        T: Future + Send + 'static,
    {
    }

    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    tokio_spawn_imitation(async move {
        contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await
            .unwrap();
    });
    Ok(())
}

#[tokio::test]
async fn test_contract_set_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let res = lib_contract_instance.methods().increment(42).call().await?;
    assert_eq!(43, res.value);

    {
        // Should fail due to missing external contracts
        let res = contract_caller_instance
            .methods()
            .increment_from_contract(lib_contract_id, 42)
            .call()
            .await;

        assert!(matches!(
            res,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    let res = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    assert_eq!(43, res.value);
    Ok(())
}

#[tokio::test]
async fn test_output_variable_contract_id_estimation_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_test_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_methods = contract_caller_instance.methods();

    let mut multi_call_handler =
        CallHandler::new_multi_call(wallet.clone()).with_tx_policies(Default::default());

    for _ in 0..3 {
        let call_handler = contract_methods.increment_from_contract(lib_contract_id, 42);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    // add call that does not need ContractId
    let contract_methods = contract_test_instance.methods();
    let call_handler = contract_methods.get(5, 6);

    multi_call_handler = multi_call_handler.add_call(call_handler);

    let call_response = multi_call_handler
        .determine_missing_contracts(None)
        .await?
        .call::<(u64, u64, u64, u64)>()
        .await?;

    assert_eq!(call_response.value, (43, 43, 43, 11));

    Ok(())
}

#[tokio::test]
async fn test_contract_call_with_non_default_max_input() -> Result<()> {
    use fuels::{
        tx::{ConsensusParameters, TxParameters},
        types::coin::Coin,
    };

    let mut consensus_parameters = ConsensusParameters::default();
    let tx_params = TxParameters::default()
        .with_max_inputs(123)
        .with_max_size(1_000_000);
    consensus_parameters.set_tx_params(tx_params);
    let contract_params = ContractParameters::default().with_contract_max_size(1_000_000);
    consensus_parameters.set_contract_params(contract_params);

    let mut wallet = WalletUnlocked::new_random(None);

    let coins: Vec<Coin> = setup_single_asset_coins(
        wallet.address(),
        Default::default(),
        DEFAULT_NUM_COINS,
        DEFAULT_COIN_AMOUNT,
    );
    let chain_config = ChainConfig {
        consensus_parameters: consensus_parameters.clone(),
        ..ChainConfig::default()
    };

    let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
    wallet.set_provider(provider.clone());
    assert_eq!(consensus_parameters, provider.consensus_parameters().await?);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn test_add_custom_assets() -> Result<()> {
    let initial_amount = 100_000;
    let asset_base = AssetConfig {
        id: AssetId::zeroed(),
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_1 = AssetId::from([3u8; 32]);
    let asset_1 = AssetConfig {
        id: asset_id_1,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_2 = AssetId::from([1u8; 32]);
    let asset_2 = AssetConfig {
        id: asset_id_2,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let assets = vec![asset_base, asset_1, asset_2];

    let num_wallets = 2;
    let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
    let mut wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
    let wallet_1 = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet_1",
            random_salt = false,
        ),
    );

    let amount_1 = 5000;
    let amount_2 = 3000;
    let response = contract_instance
        .methods()
        .get(5, 6)
        .add_custom_asset(asset_id_1, amount_1, Some(wallet_2.address().clone()))
        .add_custom_asset(asset_id_2, amount_2, Some(wallet_2.address().clone()))
        .call()
        .await?;

    assert_eq!(response.value, 11);

    let balance_asset_1 = wallet_1.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_1.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount - amount_1);
    assert_eq!(balance_asset_2, initial_amount - amount_2);

    let balance_asset_1 = wallet_2.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_2.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount + amount_1);
    assert_eq!(balance_asset_2, initial_amount + amount_2);

    Ok(())
}

#[tokio::test]
async fn contract_load_error_messages() {
    {
        let binary_path = "sway/contracts/contract_test/out/release/no_file_on_path.bin";
        let expected_error = format!("io: file \"{binary_path}\" does not exist");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
    {
        let binary_path = "sway/contracts/contract_test/out/release/contract_test-abi.json";
        let expected_error = format!("expected \"{binary_path}\" to have '.bin' extension");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
}

#[tokio::test]
async fn test_payable_annotation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/payable_annotation"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .payable()
        .call_params(
            CallParameters::default()
                .with_amount(100)
                .with_gas_forwarded(20_000),
        )?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    // ANCHOR: non_payable_params
    let err = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_amount(100))
        .expect_err("should return error");

    assert!(matches!(err, Error::Other(s) if s.contains("assets forwarded to non-payable method")));
    // ANCHOR_END: non_payable_params

    let response = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_gas_forwarded(20_000))?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    Ok(())
}

#[tokio::test]
async fn multi_call_from_calls_with_different_account_types() -> Result<()> {
    use fuels::prelude::*;

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallet = WalletUnlocked::new_random(None);
    let predicate = Predicate::from_code(vec![]);

    let contract_methods_wallet =
        MyContract::new(Bech32ContractId::default(), wallet.clone()).methods();
    let contract_methods_predicate =
        MyContract::new(Bech32ContractId::default(), predicate).methods();

    let call_handler_1 = contract_methods_wallet.initialize_counter(42);
    let call_handler_2 = contract_methods_predicate.get_array([42; 2]);

    let _multi_call_handler = CallHandler::new_multi_call(wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    Ok(())
}

#[tokio::test]
async fn low_level_call() -> Result<()> {
    use fuels::types::SizedAsciiString;

    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyCallerContract",
                project = "e2e/sway/contracts/low_level_caller"
            ),
            Contract(
                name = "MyTargetContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "caller_contract_instance",
            contract = "MyCallerContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "target_contract_instance",
            contract = "MyTargetContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let function_selector = encode_fn_selector("initialize_counter");
    let call_data = calldata!(42u64)?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let response = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await?;
    assert_eq!(response.value, 42);

    let function_selector = encode_fn_selector("set_value_multiple_complex");
    let call_data = calldata!(
        MyStruct {
            a: true,
            b: [1, 2, 3],
        },
        SizedAsciiString::<4>::try_from("fuel")?
    )?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let result_uint = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await
        .unwrap()
        .value;

    let result_bool = target_contract_instance
        .methods()
        .get_bool_value()
        .call()
        .await
        .unwrap()
        .value;

    let result_str = target_contract_instance
        .methods()
        .get_str_value()
        .call()
        .await
        .unwrap()
        .value;

    assert_eq!(result_uint, 42);
    assert!(result_bool);
    assert_eq!(result_str, "fuel");

    Ok(())
}

#[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
#[test]
fn db_rocksdb() {
    use std::{fs, str::FromStr};

    use fuels::{
        accounts::wallet::WalletUnlocked,
        client::{PageDirection, PaginationRequest},
        crypto::SecretKey,
        prelude::{setup_test_provider, DbType, Error, ViewOnlyAccount, DEFAULT_COIN_AMOUNT},
    };

    let temp_dir = tempfile::tempdir().expect("failed to make tempdir");
    let temp_dir_name = temp_dir
        .path()
        .file_name()
        .expect("failed to get file name")
        .to_string_lossy()
        .to_string();
    let temp_database_path = temp_dir.path().join("db");

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let _ = temp_dir;
            let wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            const NUMBER_OF_ASSETS: u64 = 2;
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let chain_config = ChainConfig {
                chain_name: temp_dir_name.clone(),
                consensus_parameters: Default::default(),
                ..ChainConfig::local_testnet()
            };

            let (coins, _) = setup_multiple_assets_coins(
                wallet.address(),
                NUMBER_OF_ASSETS,
                DEFAULT_NUM_COINS,
                DEFAULT_COIN_AMOUNT,
            );

            let provider =
                setup_test_provider(coins.clone(), vec![], Some(node_config), Some(chain_config))
                    .await?;

            provider.produce_blocks(2, None).await?;

            Ok::<(), Error>(())
        })
        .unwrap();

    // The runtime needs to be terminated because the node can currently only be killed when the runtime itself shuts down.

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let provider = setup_test_provider(vec![], vec![], Some(node_config), None).await?;
            // the same wallet that was used when rocksdb was built. When we connect it to the provider, we expect it to have the same amount of assets
            let mut wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            wallet.set_provider(provider.clone());

            let blocks = provider
                .get_blocks(PaginationRequest {
                    cursor: None,
                    results: 10,
                    direction: PageDirection::Forward,
                })
                .await?
                .results;

            assert_eq!(blocks.len(), 3);
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(wallet.get_balances().await?.len(), 2);

            fs::remove_dir_all(
                temp_database_path
                    .parent()
                    .expect("db parent folder does not exist"),
            )?;

            Ok::<(), Error>(())
        })
        .unwrap();
}

#[tokio::test]
async fn can_configure_decoding_of_contract_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: Will not work if max_tokens not big enough
        methods.i_return_a_1k_el_array().with_decoder_config(DecoderConfig{max_tokens: 100, ..Default::default()}).call().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Single call: Works when limit is bumped
        let result = methods
            .i_return_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(result, [0; 1000]);
    }
    {
        // Multi call: Will not work if max_tokens not big enough
        CallHandler::new_multi_call(wallet.clone())
         .add_call(methods.i_return_a_1k_el_array())
         .with_decoder_config(DecoderConfig { max_tokens: 100, ..Default::default() })
         .call::<([u8; 1000],)>().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Multi call: Works when configured
        CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_return_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<([u8; 1000],)>()
            .await
            .unwrap();
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let submitted_tx = contract_methods.get(1, 2).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;

    assert_eq!(value, 3);

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let handle = multi_call_handler.submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let (val_1, val_2): (u64, u64) = handle.response().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_heap_type_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vector_output"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        let call_handler_1 = contract_instance.methods().u8_in_vec(5);
        let call_handler_2 = contract_instance_2.methods().get_single(7);
        let call_handler_3 = contract_instance.methods().u8_in_vec(3);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2)
            .add_call(call_handler_3);

        let (val_1, val_2, val_3): (Vec<u8>, u64, Vec<u8>) = multi_call_handler.call().await?.value;

        assert_eq!(val_1, vec![0, 1, 2, 3, 4]);
        assert_eq!(val_2, 7);
        assert_eq!(val_3, vec![0, 1, 2]);
    }

    Ok(())
}

#[tokio::test]
async fn heap_types_correctly_offset_in_create_transactions_w_storage_slots() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Predicate(
            name = "MyPredicate",
            project = "e2e/sway/types/predicates/predicate_vector"
        ),),
    );

    let provider = wallet.try_provider()?.clone();
    let data = MyPredicateEncoder::default().encode_data(18, 24, vec![2, 4, 42])?;
    let predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(data)
    .with_provider(provider);

    wallet
        .transfer(
            predicate.address(),
            10_000,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await?;

    // if the contract is successfully deployed then the predicate was unlocked. This further means
    // the offsets were setup correctly since the predicate uses heap types in its arguments.
    // Storage slots were loaded automatically by default
    Contract::load_from(
        "sway/contracts/storage/out/release/storage.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    Ok(())
}

#[tokio::test]
async fn test_arguments_with_gas_forwarded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vectors"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let x = 128;
    let vec_input = vec![0, 1, 2];
    {
        let response = contract_instance
            .methods()
            .get_single(x)
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;

        assert_eq!(response.value, x);
    }
    {
        contract_instance_2
            .methods()
            .u32_vec(vec_input.clone())
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;
    }
    {
        let call_handler_1 = contract_instance.methods().get_single(x);
        let call_handler_2 = contract_instance_2.methods().u32_vec(vec_input);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let (value, _): (u64, ()) = multi_call_handler.call().await?.value;

        assert_eq!(value, x);
    }

    Ok(())
}

#[tokio::test]
async fn contract_custom_call_no_signatures_strategy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let provider = wallet.try_provider()?;

    let counter = 42;
    let call_handler = contract_instance.methods().initialize_counter(counter);

    let mut tb = call_handler.transaction_builder().await?;

    let base_asset_id = *provider.consensus_parameters().await?.base_asset_id();

    let amount = 10;
    let consensus_parameters = provider.consensus_parameters().await?;
    let new_base_inputs = wallet
        .get_asset_inputs_for_amount(base_asset_id, amount, None)
        .await?;
    tb.inputs_mut().extend(new_base_inputs);
    tb.outputs_mut()
        .push(Output::change(wallet.address().into(), 0, base_asset_id));

    // ANCHOR: tb_no_signatures_strategy
    let mut tx = tb
        .with_build_strategy(ScriptBuildStrategy::NoSignatures)
        .build(provider)
        .await?;
    // ANCHOR: tx_sign_with
    tx.sign_with(&wallet, consensus_parameters.chain_id())
        .await?;
    // ANCHOR_END: tx_sign_with
    // ANCHOR_END: tb_no_signatures_strategy

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;

    let tx_status = provider.tx_status(&tx_id).await?;

    let response = call_handler.get_response_from(tx_status)?;

    assert_eq!(counter, response.value);

    Ok(())
}

#[tokio::test]
async fn contract_encoder_config_is_applied() -> Result<()> {
    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Wallets("wallet")
    );
    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let instance = TestContract::new(contract_id.clone(), wallet.clone());

    {
        let _encoding_ok = instance
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let instance_with_encoder_config = instance.with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));

        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));
    }

    Ok(())
}

#[tokio::test]
async fn test_reentrant_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LibContractCaller",
            project = "e2e/sway/contracts/lib_contract_caller"
        ),),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = contract_caller_instance.contract_id();
    let response = contract_caller_instance
        .methods()
        .re_entrant(contract_id, true)
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn msg_sender_gas_estimation_issue() {
    // Gas estimation requires an input of the base asset. If absent, a fake input is
    // added. However, if a non-base coin is present and the fake input introduces a
    // second owner, it causes the `msg_sender` sway fn to fail. This leads
    // to a premature failure in gas estimation, risking transaction failure due to
    // a low gas limit.
    let mut wallet = WalletUnlocked::new_random(None);

    let (coins, ids) =
        setup_multiple_assets_coins(wallet.address(), 2, DEFAULT_NUM_COINS, DEFAULT_COIN_AMOUNT);

    let provider = setup_test_provider(coins, vec![], None, None)
        .await
        .unwrap();
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/msg_methods"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let asset_id = ids[0];

    // The fake coin won't be added if we add a base asset, so let's not do that
    assert!(
        asset_id
            != *provider
                .consensus_parameters()
                .await
                .unwrap()
                .base_asset_id()
    );
    let call_params = CallParameters::default()
        .with_amount(100)
        .with_asset_id(asset_id);

    contract_instance
        .methods()
        .message_sender()
        .call_params(call_params)
        .unwrap()
        .call()
        .await
        .unwrap();
}

#[tokio::test]
async fn variable_output_estimation_is_optimized() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/var_outputs"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_methods = contract_instance.methods();

    let coins = 252;
    let recipient = Identity::Address(wallet.address().into());
    let start = Instant::now();
    let _ = contract_methods
        .mint(coins, recipient)
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    // debug builds are slower (20x for `fuel-core-lib`, 4x for a release-fuel-core-binary)
    // we won't validate in that case so we don't have to maintain two expectations
    if !cfg!(debug_assertions) {
        let elapsed = start.elapsed().as_secs();
        let limit = 2;
        if elapsed > limit {
            panic!("Estimation took too long ({elapsed}). Limit is {limit}");
        }
    }

    Ok(())
}

async fn setup_node_with_high_price() -> Result<Vec<WalletUnlocked>> {
    let wallet_config = WalletsConfig::new(None, None, None);
    let fee_parameters = FeeParameters::V1(FeeParametersV1 {
        gas_price_factor: 92000,
        gas_per_byte: 63,
    });
    let consensus_parameters = ConsensusParameters::V1(ConsensusParametersV1 {
        fee_params: fee_parameters,
        ..Default::default()
    });
    let node_config = Some(NodeConfig {
        starting_gas_price: 1100,
        ..NodeConfig::default()
    });
    let chain_config = ChainConfig {
        consensus_parameters,
        ..ChainConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(wallet_config, node_config, Some(chain_config))
            .await?;

    Ok(wallets)
}

#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);

    let response = MyContract::new(contract_id, no_funds_wallet.clone())
        .methods()
        .get(5, 6)
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn simulations_can_be_made_without_coins_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let contract_instance = MyContract::new(contract_id, no_funds_wallet.clone());

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.get(1, 2);
    let call_handler_2 = contract_methods.get(3, 4);

    let mut multi_call_handler = CallHandler::new_multi_call(no_funds_wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let value: (u64, u64) = multi_call_handler
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value, (3, 7));

    Ok(())
}

#[tokio::test]
async fn contract_call_with_non_zero_base_asset_id_and_tip() -> Result<()> {
    use fuels::{prelude::*, tx::ConsensusParameters};

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let asset_id = AssetId::new([1; 32]);

    let mut consensus_parameters = ConsensusParameters::default();
    consensus_parameters.set_base_asset_id(asset_id);

    let config = ChainConfig {
        consensus_parameters,
        ..Default::default()
    };

    let asset_base = AssetConfig {
        id: asset_id,
        num_coins: 1,
        coin_amount: 10_000,
    };

    let wallet_config = WalletsConfig::new_multiple_assets(1, vec![asset_base]);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, Some(config)).await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_tip(10))
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn max_fee_estimation_respects_tolerance() -> Result<()> {
    use fuels::prelude::*;

    let mut call_wallet = WalletUnlocked::new_random(None);

    let call_coins = setup_single_asset_coins(call_wallet.address(), AssetId::BASE, 1000, 1);

    let mut deploy_wallet = WalletUnlocked::new_random(None);
    let deploy_coins =
        setup_single_asset_coins(deploy_wallet.address(), AssetId::BASE, 1, 1_000_000);

    let provider =
        setup_test_provider([call_coins, deploy_coins].concat(), vec![], None, None).await?;

    call_wallet.set_provider(provider.clone());
    deploy_wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            wallet = "deploy_wallet",
            contract = "MyContract",
            random_salt = false,
        )
    );
    let contract_instance = contract_instance.with_account(call_wallet.clone());

    let max_fee_from_tx = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            let builder = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap();

            assert_eq!(
                builder.max_fee_estimation_tolerance, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
                "Expected pre-set tolerance"
            );

            builder
                .with_max_fee_estimation_tolerance(tolerance)
                .build(&provider)
                .await
                .unwrap()
                .max_fee()
                .unwrap()
        }
    };

    let max_fee_from_builder = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance)
                .estimate_max_fee(&provider)
                .await
                .unwrap()
        }
    };

    let base_amount_in_inputs = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let call_wallet = &call_wallet;
        async move {
            let mut tb = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance);

            call_wallet.adjust_for_fee(&mut tb, 0).await.unwrap();
            tb.inputs
                .iter()
                .filter_map(|input: &Input| match input {
                    Input::ResourceSigned { resource }
                        if resource.coin_asset_id().unwrap() == AssetId::BASE =>
                    {
                        Some(resource.amount())
                    }
                    _ => None,
                })
                .sum::<u64>()
        }
    };

    let no_increase_max_fee = max_fee_from_tx(0.0).await;
    let increased_max_fee = max_fee_from_tx(2.00).await;

    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let no_increase_max_fee = max_fee_from_builder(0.0).await;
    let increased_max_fee = max_fee_from_builder(2.00).await;
    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let normal_base_asset = base_amount_in_inputs(0.0).await;
    let more_base_asset_due_to_bigger_tolerance = base_amount_in_inputs(5.00).await;
    assert!(more_base_asset_due_to_bigger_tolerance > normal_base_asset);

    Ok(())
}

#[tokio::test]
async fn blob_contract_deployment() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
    ));

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract_size = std::fs::metadata(contract_binary)
        .expect("contract file not found")
        .len();

    assert!(
         contract_size > 150_000,
         "the testnet size limit was around 100kB, we want a contract bigger than that to reflect prod (current: {contract_size}B)"
     );

    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::new(Some(2), None, None), None, None)
            .await?;

    let provider = wallets[0].provider().unwrap().clone();

    let consensus_parameters = provider.consensus_parameters().await?;

    let contract_max_size = consensus_parameters.contract_params().contract_max_size();
    assert!(
         contract_size > contract_max_size,
         "this test should ideally be run with a contract bigger than the max contract size ({contract_max_size}B) so that we know deployment couldn't have happened without blobs"
     );

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallets[0], TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn regular_contract_can_be_deployed() -> Result<()> {
    // given
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
    );

    let contract_binary = "sway/contracts/contract_test/out/release/contract_test.bin";

    // when
    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    // then
    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance
        .methods()
        .get_counter()
        .call()
        .await?
        .value;

    assert_eq!(response, 0);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_be_deployed_directly() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_upload_blobs_separately_then_deploy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    let blob_ids = contract.blob_ids();

    // if this were an example for the user we'd just call `deploy` on the contract above
    // this way we are testing that the blobs were really deployed above, otherwise the following
    // would fail
    let contract_id = Contract::loader_from_blob_ids(
        blob_ids.to_vec(),
        contract.salt(),
        contract.storage_slots().to_vec(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_blob_already_uploaded_not_an_issue() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?;

    // this will upload blobs
    contract
        .clone()
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    // this will try to upload the blobs but skip upon encountering an error
    let contract_id = contract
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .something()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_storage_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_storage_slots = contract.storage_slots().to_vec();

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";
    let proxy_contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let combined_storage_slots = [&contract_storage_slots, proxy_contract.storage_slots()].concat();

    let proxy_id = proxy_contract
        .with_storage_slots(combined_storage_slots)
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?
        .value;

    assert_eq!(response, 42);

    let _res = proxy
        .methods()
        .write_some_u64(36)
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 36);

    Ok(())
}\n```

In this example, three contracts are deployed on the same provider using the wallet generated by the Wallets command. The second and third macros use the same contract but have different IDs because of the deployment with salt. Both of them can call the first contract by using their ID.

In addition, you can manually create the wallet variable and then use it inside the macro. This is useful if you want to create custom wallets or providers but still want to use the macro to reduce boilerplate code. Below is an example of this approach.

```rust\nuse std::time::Duration;

use fuel_tx::{
    consensus_parameters::{ConsensusParametersV1, FeeParametersV1},
    ConsensusParameters, FeeParameters, Output,
};
use fuels::{
    core::codec::{calldata, encode_fn_selector, DecoderConfig, EncoderConfig},
    prelude::*,
    programs::DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
    tx::ContractParameters,
    types::{errors::transaction::Reason, input::Input, Bits256, Identity},
};
use tokio::time::Instant;

#[tokio::test]
async fn test_multiple_args() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Make sure we can call the contract with multiple arguments
    let contract_methods = contract_instance.methods();
    let response = contract_methods.get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    let t = MyType { x: 5, y: 6 };
    let response = contract_methods.get_alt(t.clone()).call().await?;
    assert_eq!(response.value, t);

    let response = contract_methods.get_single(5).call().await?;
    assert_eq!(response.value, 5);
    Ok(())
}

#[tokio::test]
async fn test_contract_calling_contract() -> Result<()> {
    // Tests a contract call that calls another contract (FooCaller calls FooContract underneath)
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "lib_contract_instance2",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();
    let lib_contract_id2 = lib_contract_instance2.contract_id();

    // Call the contract directly. It increments the given value.
    let response = lib_contract_instance.methods().increment(42).call().await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance
        .methods()
        .increment_from_contracts(lib_contract_id, lib_contract_id2, 42)
        // Note that the two lib_contract_instances have different types
        .with_contracts(&[&lib_contract_instance, &lib_contract_instance2])
        .call()
        .await?;

    assert_eq!(86, response.value);

    // ANCHOR: external_contract
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;
    // ANCHOR_END: external_contract

    assert_eq!(43, response.value);

    // ANCHOR: external_contract_ids
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contract_ids(&[lib_contract_id.clone()])
        .call()
        .await?;
    // ANCHOR_END: external_contract_ids

    assert_eq!(43, response.value);
    Ok(())
}

#[tokio::test]
async fn test_reverting_transaction() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "RevertContract",
            project = "e2e/sway/contracts/revert_transaction_error"
        )),
        Deploy(
            name = "contract_instance",
            contract = "RevertContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance
        .methods()
        .make_transaction_fail(true)
        .call()
        .await;

    assert!(matches!(
        response,
        Err(Error::Transaction(Reason::Reverted { revert_id, .. })) if revert_id == 128
    ));

    Ok(())
}

#[tokio::test]
async fn test_multiple_read_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MultiReadContract",
            project = "e2e/sway/contracts/multiple_read_calls"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MultiReadContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    contract_methods.store(42).call().await?;

    // Use "simulate" because the methods don't actually
    // run a transaction, but just a dry-run
    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);

    let stored = contract_methods
        .read()
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(stored.value, 42);
    Ok(())
}

#[tokio::test]
async fn test_multi_call_beginner() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let (val_1, val_2): (u64, u64) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_multi_call_pro() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let my_type_1 = MyType { x: 1, y: 2 };
    let my_type_2 = MyType { x: 3, y: 4 };

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(5);
    let call_handler_2 = contract_methods.get_single(6);
    let call_handler_3 = contract_methods.get_alt(my_type_1.clone());
    let call_handler_4 = contract_methods.get_alt(my_type_2.clone());
    let call_handler_5 = contract_methods.get_array([7; 2]);
    let call_handler_6 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2)
        .add_call(call_handler_3)
        .add_call(call_handler_4)
        .add_call(call_handler_5)
        .add_call(call_handler_6);

    let (val_1, val_2, type_1, type_2, array_1, array_2): (
        u64,
        u64,
        MyType,
        MyType,
        [u64; 2],
        [u64; 2],
    ) = multi_call_handler.call().await?.value;

    assert_eq!(val_1, 5);
    assert_eq!(val_2, 6);
    assert_eq!(type_1, my_type_1);
    assert_eq!(type_2, my_type_2);
    assert_eq!(array_1, [7; 2]);
    assert_eq!(array_2, [42; 2]);

    Ok(())
}

#[tokio::test]
async fn test_contract_call_fee_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let gas_limit = 800;
    let tolerance = Some(0.2);
    let block_horizon = Some(1);
    let expected_gas_used = 960;
    let expected_metered_bytes_size = 824;

    let estimated_transaction_cost = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit))
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?;

    assert_eq!(estimated_transaction_cost.gas_used, expected_gas_used);
    assert_eq!(
        estimated_transaction_cost.metered_bytes_size,
        expected_metered_bytes_size
    );

    Ok(())
}

#[tokio::test]
async fn contract_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_methods = contract_instance.methods();

    let tolerance = Some(0.0);
    let block_horizon = Some(1);

    let estimated_gas_used = contract_methods
        .initialize_counter(42)
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = contract_methods
        .initialize_counter(42)
        .call()
        .await?
        .gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn mult_call_has_same_estimated_and_used_gas() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.initialize_counter(42);
    let call_handler_2 = contract_methods.get_array([42; 2]);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let tolerance = Some(0.0);
    let block_horizon = Some(1);
    let estimated_gas_used = multi_call_handler
        .estimate_transaction_cost(tolerance, block_horizon)
        .await?
        .gas_used;

    let gas_used = multi_call_handler.call::<(u64, [u64; 2])>().await?.gas_used;

    assert_eq!(estimated_gas_used, gas_used);
    Ok(())
}

#[tokio::test]
async fn contract_method_call_respects_maturity() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "BlockHeightContract",
            project = "e2e/sway/contracts/transaction_block_height"
        )),
        Deploy(
            name = "contract_instance",
            contract = "BlockHeightContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let call_w_maturity = |maturity| {
        contract_instance
            .methods()
            .calling_this_will_produce_a_block()
            .with_tx_policies(TxPolicies::default().with_maturity(maturity))
    };

    call_w_maturity(1).call().await.expect(
        "should have passed since we're calling with a maturity \
         that is less or equal to the current block height",
    );

    call_w_maturity(3).call().await.expect_err(
        "should have failed since we're calling with a maturity \
         that is greater than the current block height",
    );

    Ok(())
}

#[tokio::test]
async fn test_auth_msg_sender_from_sdk() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "AuthContract",
            project = "e2e/sway/contracts/auth_testing_contract"
        )),
        Deploy(
            name = "contract_instance",
            contract = "AuthContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Contract returns true if `msg_sender()` matches `wallet.address()`.
    let response = contract_instance
        .methods()
        .check_msg_sender(wallet.address())
        .call()
        .await?;

    assert!(response.value);
    Ok(())
}

#[tokio::test]
async fn test_large_return_data() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/large_return_data"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();
    let res = contract_methods.get_id().call().await?;

    assert_eq!(
        res.value.0,
        [
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ]
    );

    // One word-sized string
    let res = contract_methods.get_small_string().call().await?;
    assert_eq!(res.value, "gggggggg");

    // Two word-sized string
    let res = contract_methods.get_large_string().call().await?;
    assert_eq!(res.value, "ggggggggg");

    // Large struct will be bigger than a `WORD`.
    let res = contract_methods.get_large_struct().call().await?;
    assert_eq!(res.value.foo, 12);
    assert_eq!(res.value.bar, 42);

    // Array will be returned in `ReturnData`.
    let res = contract_methods.get_large_array().call().await?;
    assert_eq!(res.value, [1, 2]);

    let res = contract_methods.get_contract_id().call().await?;

    // First `value` is from `CallResponse`.
    // Second `value` is from the `ContractId` type.
    assert_eq!(
        res.value,
        ContractId::from([
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        ])
    );
    Ok(())
}

#[tokio::test]
async fn can_handle_function_called_new() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().new().call().await?.value;

    assert_eq!(response, 12345);
    Ok(())
}

#[tokio::test]
async fn test_contract_setup_macro_deploy_with_salt() -> Result<()> {
    // ANCHOR: contract_setup_macro_multi
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
        Deploy(
            name = "contract_caller_instance2",
            contract = "LibContractCaller",
            wallet = "wallet",
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_caller_id = contract_caller_instance.contract_id();

    let contract_caller_id2 = contract_caller_instance2.contract_id();

    // Because we deploy with salt, we can deploy the same contract multiple times
    assert_ne!(contract_caller_id, contract_caller_id2);

    // The first contract can be called because they were deployed on the same provider
    let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);

    let response = contract_caller_instance2
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .with_contracts(&[&lib_contract_instance])
        .call()
        .await?;

    assert_eq!(43, response.value);
    // ANCHOR_END: contract_setup_macro_multi

    Ok(())
}

#[tokio::test]
async fn test_wallet_getter() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    assert_eq!(contract_instance.account().address(), wallet.address());
    //`contract_id()` is tested in
    // async fn test_contract_calling_contract() -> Result<()> {
    Ok(())
}

#[tokio::test]
async fn test_connect_wallet() -> Result<()> {
    // ANCHOR: contract_setup_macro_manual_wallet
    let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));

    let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
    let wallet = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    // ANCHOR_END: contract_setup_macro_manual_wallet

    // pay for call with wallet
    let tx_policies = TxPolicies::default()
        .with_tip(100)
        .with_script_gas_limit(1_000_000);

    contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm that funds have been deducted
    let wallet_balance = wallet.get_asset_balance(&Default::default()).await?;
    assert!(DEFAULT_COIN_AMOUNT > wallet_balance);

    // pay for call with wallet_2
    contract_instance
        .with_account(wallet_2.clone())
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    // confirm there are no changes to wallet, wallet_2 has been charged
    let wallet_balance_second_call = wallet.get_asset_balance(&Default::default()).await?;
    let wallet_2_balance = wallet_2.get_asset_balance(&Default::default()).await?;
    assert_eq!(wallet_balance_second_call, wallet_balance);
    assert!(DEFAULT_COIN_AMOUNT > wallet_2_balance);

    Ok(())
}

async fn setup_output_variable_estimation_test() -> Result<(
    Vec<WalletUnlocked>,
    [Identity; 3],
    AssetId,
    Bech32ContractId,
)> {
    let wallet_config = WalletsConfig::new(Some(3), None, None);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;

    let contract_id = Contract::load_from(
        "sway/contracts/token_ops/out/release/token_ops.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallets[0], TxPolicies::default())
    .await?;

    let mint_asset_id = contract_id.asset_id(&Bits256::zeroed());
    let addresses = wallets
        .iter()
        .map(|wallet| wallet.address().into())
        .collect::<Vec<_>>()
        .try_into()
        .unwrap();

    Ok((wallets, addresses, mint_asset_id, contract_id))
}

#[tokio::test]
async fn test_output_variable_estimation() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());
    let contract_methods = contract_instance.methods();
    let amount = 1000;

    {
        // Should fail due to lack of output variables
        let response = contract_methods
            .mint_to_addresses(amount, addresses)
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    {
        // Should add 3 output variables automatically
        let _ = contract_methods
            .mint_to_addresses(amount, addresses)
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .call()
            .await?;

        for wallet in wallets.iter() {
            let balance = wallet.get_asset_balance(&mint_asset_id).await?;
            assert_eq!(balance, amount);
        }
    }

    Ok(())
}

#[tokio::test]
async fn test_output_variable_estimation_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
    ));

    let (wallets, addresses, mint_asset_id, contract_id) =
        setup_output_variable_estimation_test().await?;

    let contract_instance = MyContract::new(contract_id.clone(), wallets[0].clone());
    let contract_methods = contract_instance.methods();
    const NUM_OF_CALLS: u64 = 3;
    let amount = 1000;
    let total_amount = amount * NUM_OF_CALLS;

    let mut multi_call_handler = CallHandler::new_multi_call(wallets[0].clone());
    for _ in 0..NUM_OF_CALLS {
        let call_handler = contract_methods.mint_to_addresses(amount, addresses);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    wallets[0]
        .force_transfer_to_contract(
            &contract_id,
            total_amount,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await
        .unwrap();

    let base_layer_address = Bits256([1u8; 32]);
    let call_handler = contract_methods.send_message(base_layer_address, amount);
    multi_call_handler = multi_call_handler.add_call(call_handler);

    let _ = multi_call_handler
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call::<((), (), ())>()
        .await?;

    for wallet in wallets.iter() {
        let balance = wallet.get_asset_balance(&mint_asset_id).await?;
        assert_eq!(balance, 3 * amount);
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_instance_get_balances() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let (coins, asset_ids) = setup_multiple_assets_coins(wallet.address(), 2, 4, 8);

    let random_asset_id = &asset_ids[1];
    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_id = contract_instance.contract_id();

    // Check the current balance of the contract with id 'contract_id'
    let contract_balances = contract_instance.get_balances().await?;
    assert!(contract_balances.is_empty());

    // Transfer an amount to the contract
    let amount = 8;
    wallet
        .force_transfer_to_contract(contract_id, amount, *random_asset_id, TxPolicies::default())
        .await?;

    // Check that the contract now has 1 coin
    let contract_balances = contract_instance.get_balances().await?;
    assert_eq!(contract_balances.len(), 1);

    let random_asset_balance = contract_balances.get(random_asset_id).unwrap();
    assert_eq!(*random_asset_balance, amount);

    Ok(())
}

#[tokio::test]
async fn contract_call_futures_implement_send() -> Result<()> {
    use std::future::Future;

    fn tokio_spawn_imitation<T>(_: T)
    where
        T: Future + Send + 'static,
    {
    }

    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    tokio_spawn_imitation(async move {
        contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await
            .unwrap();
    });
    Ok(())
}

#[tokio::test]
async fn test_contract_set_estimation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let lib_contract_id = lib_contract_instance.contract_id();

    let res = lib_contract_instance.methods().increment(42).call().await?;
    assert_eq!(43, res.value);

    {
        // Should fail due to missing external contracts
        let res = contract_caller_instance
            .methods()
            .increment_from_contract(lib_contract_id, 42)
            .call()
            .await;

        assert!(matches!(
            res,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
    }

    let res = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id, 42)
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    assert_eq!(43, res.value);
    Ok(())
}

#[tokio::test]
async fn test_output_variable_contract_id_estimation_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "LibContract",
                project = "e2e/sway/contracts/lib_contract"
            ),
            Contract(
                name = "LibContractCaller",
                project = "e2e/sway/contracts/lib_contract_caller"
            ),
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "lib_contract_instance",
            contract = "LibContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_test_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let lib_contract_id = lib_contract_instance.contract_id();

    let contract_methods = contract_caller_instance.methods();

    let mut multi_call_handler =
        CallHandler::new_multi_call(wallet.clone()).with_tx_policies(Default::default());

    for _ in 0..3 {
        let call_handler = contract_methods.increment_from_contract(lib_contract_id, 42);
        multi_call_handler = multi_call_handler.add_call(call_handler);
    }

    // add call that does not need ContractId
    let contract_methods = contract_test_instance.methods();
    let call_handler = contract_methods.get(5, 6);

    multi_call_handler = multi_call_handler.add_call(call_handler);

    let call_response = multi_call_handler
        .determine_missing_contracts(None)
        .await?
        .call::<(u64, u64, u64, u64)>()
        .await?;

    assert_eq!(call_response.value, (43, 43, 43, 11));

    Ok(())
}

#[tokio::test]
async fn test_contract_call_with_non_default_max_input() -> Result<()> {
    use fuels::{
        tx::{ConsensusParameters, TxParameters},
        types::coin::Coin,
    };

    let mut consensus_parameters = ConsensusParameters::default();
    let tx_params = TxParameters::default()
        .with_max_inputs(123)
        .with_max_size(1_000_000);
    consensus_parameters.set_tx_params(tx_params);
    let contract_params = ContractParameters::default().with_contract_max_size(1_000_000);
    consensus_parameters.set_contract_params(contract_params);

    let mut wallet = WalletUnlocked::new_random(None);

    let coins: Vec<Coin> = setup_single_asset_coins(
        wallet.address(),
        Default::default(),
        DEFAULT_NUM_COINS,
        DEFAULT_COIN_AMOUNT,
    );
    let chain_config = ChainConfig {
        consensus_parameters: consensus_parameters.clone(),
        ..ChainConfig::default()
    };

    let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
    wallet.set_provider(provider.clone());
    assert_eq!(consensus_parameters, provider.consensus_parameters().await?);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let response = contract_instance.methods().get(5, 6).call().await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn test_add_custom_assets() -> Result<()> {
    let initial_amount = 100_000;
    let asset_base = AssetConfig {
        id: AssetId::zeroed(),
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_1 = AssetId::from([3u8; 32]);
    let asset_1 = AssetConfig {
        id: asset_id_1,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let asset_id_2 = AssetId::from([1u8; 32]);
    let asset_2 = AssetConfig {
        id: asset_id_2,
        num_coins: 1,
        coin_amount: initial_amount,
    };

    let assets = vec![asset_base, asset_1, asset_2];

    let num_wallets = 2;
    let wallet_config = WalletsConfig::new_multiple_assets(num_wallets, assets);
    let mut wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
    let wallet_1 = wallets.pop().unwrap();
    let wallet_2 = wallets.pop().unwrap();

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "MyContract",
            wallet = "wallet_1",
            random_salt = false,
        ),
    );

    let amount_1 = 5000;
    let amount_2 = 3000;
    let response = contract_instance
        .methods()
        .get(5, 6)
        .add_custom_asset(asset_id_1, amount_1, Some(wallet_2.address().clone()))
        .add_custom_asset(asset_id_2, amount_2, Some(wallet_2.address().clone()))
        .call()
        .await?;

    assert_eq!(response.value, 11);

    let balance_asset_1 = wallet_1.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_1.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount - amount_1);
    assert_eq!(balance_asset_2, initial_amount - amount_2);

    let balance_asset_1 = wallet_2.get_asset_balance(&asset_id_1).await?;
    let balance_asset_2 = wallet_2.get_asset_balance(&asset_id_2).await?;
    assert_eq!(balance_asset_1, initial_amount + amount_1);
    assert_eq!(balance_asset_2, initial_amount + amount_2);

    Ok(())
}

#[tokio::test]
async fn contract_load_error_messages() {
    {
        let binary_path = "sway/contracts/contract_test/out/release/no_file_on_path.bin";
        let expected_error = format!("io: file \"{binary_path}\" does not exist");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
    {
        let binary_path = "sway/contracts/contract_test/out/release/contract_test-abi.json";
        let expected_error = format!("expected \"{binary_path}\" to have '.bin' extension");

        let error = Contract::load_from(binary_path, LoadConfiguration::default())
            .expect_err("should have failed");

        assert_eq!(error.to_string(), expected_error);
    }
}

#[tokio::test]
async fn test_payable_annotation() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/payable_annotation"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let response = contract_methods
        .payable()
        .call_params(
            CallParameters::default()
                .with_amount(100)
                .with_gas_forwarded(20_000),
        )?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    // ANCHOR: non_payable_params
    let err = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_amount(100))
        .expect_err("should return error");

    assert!(matches!(err, Error::Other(s) if s.contains("assets forwarded to non-payable method")));
    // ANCHOR_END: non_payable_params

    let response = contract_methods
        .non_payable()
        .call_params(CallParameters::default().with_gas_forwarded(20_000))?
        .call()
        .await?;

    assert_eq!(response.value, 42);

    Ok(())
}

#[tokio::test]
async fn multi_call_from_calls_with_different_account_types() -> Result<()> {
    use fuels::prelude::*;

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallet = WalletUnlocked::new_random(None);
    let predicate = Predicate::from_code(vec![]);

    let contract_methods_wallet =
        MyContract::new(Bech32ContractId::default(), wallet.clone()).methods();
    let contract_methods_predicate =
        MyContract::new(Bech32ContractId::default(), predicate).methods();

    let call_handler_1 = contract_methods_wallet.initialize_counter(42);
    let call_handler_2 = contract_methods_predicate.get_array([42; 2]);

    let _multi_call_handler = CallHandler::new_multi_call(wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    Ok(())
}

#[tokio::test]
async fn low_level_call() -> Result<()> {
    use fuels::types::SizedAsciiString;

    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "MyCallerContract",
                project = "e2e/sway/contracts/low_level_caller"
            ),
            Contract(
                name = "MyTargetContract",
                project = "e2e/sway/contracts/contract_test"
            ),
        ),
        Deploy(
            name = "caller_contract_instance",
            contract = "MyCallerContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "target_contract_instance",
            contract = "MyTargetContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let function_selector = encode_fn_selector("initialize_counter");
    let call_data = calldata!(42u64)?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let response = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await?;
    assert_eq!(response.value, 42);

    let function_selector = encode_fn_selector("set_value_multiple_complex");
    let call_data = calldata!(
        MyStruct {
            a: true,
            b: [1, 2, 3],
        },
        SizedAsciiString::<4>::try_from("fuel")?
    )?;

    caller_contract_instance
        .methods()
        .call_low_level_call(
            target_contract_instance.id(),
            Bytes(function_selector),
            Bytes(call_data),
        )
        .determine_missing_contracts(None)
        .await?
        .call()
        .await?;

    let result_uint = target_contract_instance
        .methods()
        .get_counter()
        .call()
        .await
        .unwrap()
        .value;

    let result_bool = target_contract_instance
        .methods()
        .get_bool_value()
        .call()
        .await
        .unwrap()
        .value;

    let result_str = target_contract_instance
        .methods()
        .get_str_value()
        .call()
        .await
        .unwrap()
        .value;

    assert_eq!(result_uint, 42);
    assert!(result_bool);
    assert_eq!(result_str, "fuel");

    Ok(())
}

#[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
#[test]
fn db_rocksdb() {
    use std::{fs, str::FromStr};

    use fuels::{
        accounts::wallet::WalletUnlocked,
        client::{PageDirection, PaginationRequest},
        crypto::SecretKey,
        prelude::{setup_test_provider, DbType, Error, ViewOnlyAccount, DEFAULT_COIN_AMOUNT},
    };

    let temp_dir = tempfile::tempdir().expect("failed to make tempdir");
    let temp_dir_name = temp_dir
        .path()
        .file_name()
        .expect("failed to get file name")
        .to_string_lossy()
        .to_string();
    let temp_database_path = temp_dir.path().join("db");

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let _ = temp_dir;
            let wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            const NUMBER_OF_ASSETS: u64 = 2;
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let chain_config = ChainConfig {
                chain_name: temp_dir_name.clone(),
                consensus_parameters: Default::default(),
                ..ChainConfig::local_testnet()
            };

            let (coins, _) = setup_multiple_assets_coins(
                wallet.address(),
                NUMBER_OF_ASSETS,
                DEFAULT_NUM_COINS,
                DEFAULT_COIN_AMOUNT,
            );

            let provider =
                setup_test_provider(coins.clone(), vec![], Some(node_config), Some(chain_config))
                    .await?;

            provider.produce_blocks(2, None).await?;

            Ok::<(), Error>(())
        })
        .unwrap();

    // The runtime needs to be terminated because the node can currently only be killed when the runtime itself shuts down.

    tokio::runtime::Runtime::new()
        .expect("tokio runtime failed")
        .block_on(async {
            let node_config = NodeConfig {
                database_type: DbType::RocksDb(Some(temp_database_path.clone())),
                ..NodeConfig::default()
            };

            let provider = setup_test_provider(vec![], vec![], Some(node_config), None).await?;
            // the same wallet that was used when rocksdb was built. When we connect it to the provider, we expect it to have the same amount of assets
            let mut wallet = WalletUnlocked::new_from_private_key(
                SecretKey::from_str(
                    "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
                )?,
                None,
            );

            wallet.set_provider(provider.clone());

            let blocks = provider
                .get_blocks(PaginationRequest {
                    cursor: None,
                    results: 10,
                    direction: PageDirection::Forward,
                })
                .await?
                .results;

            assert_eq!(blocks.len(), 3);
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(
                *wallet.get_balances().await?.iter().next().unwrap().1,
                DEFAULT_COIN_AMOUNT as u128
            );
            assert_eq!(wallet.get_balances().await?.len(), 2);

            fs::remove_dir_all(
                temp_database_path
                    .parent()
                    .expect("db parent folder does not exist"),
            )?;

            Ok::<(), Error>(())
        })
        .unwrap();
}

#[tokio::test]
async fn can_configure_decoding_of_contract_return() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/needs_custom_decoder"
        ),),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let methods = contract_instance.methods();
    {
        // Single call: Will not work if max_tokens not big enough
        methods.i_return_a_1k_el_array().with_decoder_config(DecoderConfig{max_tokens: 100, ..Default::default()}).call().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Single call: Works when limit is bumped
        let result = methods
            .i_return_a_1k_el_array()
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call()
            .await?
            .value;

        assert_eq!(result, [0; 1000]);
    }
    {
        // Multi call: Will not work if max_tokens not big enough
        CallHandler::new_multi_call(wallet.clone())
         .add_call(methods.i_return_a_1k_el_array())
         .with_decoder_config(DecoderConfig { max_tokens: 100, ..Default::default() })
         .call::<([u8; 1000],)>().await.expect_err(
             "should have failed because there are more tokens than what is supported by default",
         );
    }
    {
        // Multi call: Works when configured
        CallHandler::new_multi_call(wallet.clone())
            .add_call(methods.i_return_a_1k_el_array())
            .with_decoder_config(DecoderConfig {
                max_tokens: 1001,
                ..Default::default()
            })
            .call::<([u8; 1000],)>()
            .await
            .unwrap();
    }

    Ok(())
}

#[tokio::test]
async fn test_contract_submit_and_response() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_methods = contract_instance.methods();

    let submitted_tx = contract_methods.get(1, 2).submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let value = submitted_tx.response().await?.value;

    assert_eq!(value, 3);

    let contract_methods = contract_instance.methods();
    let call_handler_1 = contract_methods.get_single(7);
    let call_handler_2 = contract_methods.get_single(42);

    let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let handle = multi_call_handler.submit().await?;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let (val_1, val_2): (u64, u64) = handle.response().await?.value;

    assert_eq!(val_1, 7);
    assert_eq!(val_2, 42);

    Ok(())
}

#[tokio::test]
async fn test_heap_type_multicall() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vector_output"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    {
        let call_handler_1 = contract_instance.methods().u8_in_vec(5);
        let call_handler_2 = contract_instance_2.methods().get_single(7);
        let call_handler_3 = contract_instance.methods().u8_in_vec(3);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2)
            .add_call(call_handler_3);

        let (val_1, val_2, val_3): (Vec<u8>, u64, Vec<u8>) = multi_call_handler.call().await?.value;

        assert_eq!(val_1, vec![0, 1, 2, 3, 4]);
        assert_eq!(val_2, 7);
        assert_eq!(val_3, vec![0, 1, 2]);
    }

    Ok(())
}

#[tokio::test]
async fn heap_types_correctly_offset_in_create_transactions_w_storage_slots() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Predicate(
            name = "MyPredicate",
            project = "e2e/sway/types/predicates/predicate_vector"
        ),),
    );

    let provider = wallet.try_provider()?.clone();
    let data = MyPredicateEncoder::default().encode_data(18, 24, vec![2, 4, 42])?;
    let predicate = Predicate::load_from(
        "sway/types/predicates/predicate_vector/out/release/predicate_vector.bin",
    )?
    .with_data(data)
    .with_provider(provider);

    wallet
        .transfer(
            predicate.address(),
            10_000,
            AssetId::zeroed(),
            TxPolicies::default(),
        )
        .await?;

    // if the contract is successfully deployed then the predicate was unlocked. This further means
    // the offsets were setup correctly since the predicate uses heap types in its arguments.
    // Storage slots were loaded automatically by default
    Contract::load_from(
        "sway/contracts/storage/out/release/storage.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&predicate, TxPolicies::default())
    .await?;

    Ok(())
}

#[tokio::test]
async fn test_arguments_with_gas_forwarded() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(
            Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            ),
            Contract(
                name = "VectorOutputContract",
                project = "e2e/sway/types/contracts/vectors"
            )
        ),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
        Deploy(
            name = "contract_instance_2",
            contract = "VectorOutputContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let x = 128;
    let vec_input = vec![0, 1, 2];
    {
        let response = contract_instance
            .methods()
            .get_single(x)
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;

        assert_eq!(response.value, x);
    }
    {
        contract_instance_2
            .methods()
            .u32_vec(vec_input.clone())
            .call_params(CallParameters::default().with_gas_forwarded(4096))?
            .call()
            .await?;
    }
    {
        let call_handler_1 = contract_instance.methods().get_single(x);
        let call_handler_2 = contract_instance_2.methods().u32_vec(vec_input);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let (value, _): (u64, ()) = multi_call_handler.call().await?.value;

        assert_eq!(value, x);
    }

    Ok(())
}

#[tokio::test]
async fn contract_custom_call_no_signatures_strategy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let provider = wallet.try_provider()?;

    let counter = 42;
    let call_handler = contract_instance.methods().initialize_counter(counter);

    let mut tb = call_handler.transaction_builder().await?;

    let base_asset_id = *provider.consensus_parameters().await?.base_asset_id();

    let amount = 10;
    let consensus_parameters = provider.consensus_parameters().await?;
    let new_base_inputs = wallet
        .get_asset_inputs_for_amount(base_asset_id, amount, None)
        .await?;
    tb.inputs_mut().extend(new_base_inputs);
    tb.outputs_mut()
        .push(Output::change(wallet.address().into(), 0, base_asset_id));

    // ANCHOR: tb_no_signatures_strategy
    let mut tx = tb
        .with_build_strategy(ScriptBuildStrategy::NoSignatures)
        .build(provider)
        .await?;
    // ANCHOR: tx_sign_with
    tx.sign_with(&wallet, consensus_parameters.chain_id())
        .await?;
    // ANCHOR_END: tx_sign_with
    // ANCHOR_END: tb_no_signatures_strategy

    let tx_id = provider.send_transaction(tx).await?;
    tokio::time::sleep(Duration::from_millis(500)).await;

    let tx_status = provider.tx_status(&tx_id).await?;

    let response = call_handler.get_response_from(tx_status)?;

    assert_eq!(counter, response.value);

    Ok(())
}

#[tokio::test]
async fn contract_encoder_config_is_applied() -> Result<()> {
    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Wallets("wallet")
    );
    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let instance = TestContract::new(contract_id.clone(), wallet.clone());

    {
        let _encoding_ok = instance
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect("should not fail as it uses the default encoder config");
    }
    {
        let encoder_config = EncoderConfig {
            max_tokens: 1,
            ..Default::default()
        };
        let instance_with_encoder_config = instance.with_encoder_config(encoder_config);

        // uses 2 tokens when 1 is the limit
        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .call()
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));

        let encoding_error = instance_with_encoder_config
            .methods()
            .get(0, 1)
            .simulate(Execution::Realistic)
            .await
            .expect_err("should error");

        assert!(encoding_error.to_string().contains(
            "cannot encode contract call arguments: codec: token limit `1` reached while encoding."
        ));
    }

    Ok(())
}

#[tokio::test]
async fn test_reentrant_calls() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "LibContractCaller",
            project = "e2e/sway/contracts/lib_contract_caller"
        ),),
        Deploy(
            name = "contract_caller_instance",
            contract = "LibContractCaller",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let contract_id = contract_caller_instance.contract_id();
    let response = contract_caller_instance
        .methods()
        .re_entrant(contract_id, true)
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn msg_sender_gas_estimation_issue() {
    // Gas estimation requires an input of the base asset. If absent, a fake input is
    // added. However, if a non-base coin is present and the fake input introduces a
    // second owner, it causes the `msg_sender` sway fn to fail. This leads
    // to a premature failure in gas estimation, risking transaction failure due to
    // a low gas limit.
    let mut wallet = WalletUnlocked::new_random(None);

    let (coins, ids) =
        setup_multiple_assets_coins(wallet.address(), 2, DEFAULT_NUM_COINS, DEFAULT_COIN_AMOUNT);

    let provider = setup_test_provider(coins, vec![], None, None)
        .await
        .unwrap();
    wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/msg_methods"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let asset_id = ids[0];

    // The fake coin won't be added if we add a base asset, so let's not do that
    assert!(
        asset_id
            != *provider
                .consensus_parameters()
                .await
                .unwrap()
                .base_asset_id()
    );
    let call_params = CallParameters::default()
        .with_amount(100)
        .with_asset_id(asset_id);

    contract_instance
        .methods()
        .message_sender()
        .call_params(call_params)
        .unwrap()
        .call()
        .await
        .unwrap();
}

#[tokio::test]
async fn variable_output_estimation_is_optimized() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/var_outputs"
        )),
        Deploy(
            contract = "MyContract",
            name = "contract_instance",
            wallet = "wallet",
            random_salt = false,
        )
    );

    let contract_methods = contract_instance.methods();

    let coins = 252;
    let recipient = Identity::Address(wallet.address().into());
    let start = Instant::now();
    let _ = contract_methods
        .mint(coins, recipient)
        .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
        .call()
        .await?;

    // debug builds are slower (20x for `fuel-core-lib`, 4x for a release-fuel-core-binary)
    // we won't validate in that case so we don't have to maintain two expectations
    if !cfg!(debug_assertions) {
        let elapsed = start.elapsed().as_secs();
        let limit = 2;
        if elapsed > limit {
            panic!("Estimation took too long ({elapsed}). Limit is {limit}");
        }
    }

    Ok(())
}

async fn setup_node_with_high_price() -> Result<Vec<WalletUnlocked>> {
    let wallet_config = WalletsConfig::new(None, None, None);
    let fee_parameters = FeeParameters::V1(FeeParametersV1 {
        gas_price_factor: 92000,
        gas_per_byte: 63,
    });
    let consensus_parameters = ConsensusParameters::V1(ConsensusParametersV1 {
        fee_params: fee_parameters,
        ..Default::default()
    });
    let node_config = Some(NodeConfig {
        starting_gas_price: 1100,
        ..NodeConfig::default()
    });
    let chain_config = ChainConfig {
        consensus_parameters,
        ..ChainConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(wallet_config, node_config, Some(chain_config))
            .await?;

    Ok(wallets)
}

#[tokio::test]
async fn simulations_can_be_made_without_coins() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);

    let response = MyContract::new(contract_id, no_funds_wallet.clone())
        .methods()
        .get(5, 6)
        .simulate(Execution::StateReadOnly)
        .await?;

    assert_eq!(response.value, 11);

    Ok(())
}

#[tokio::test]
async fn simulations_can_be_made_without_coins_multicall() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let wallets = setup_node_with_high_price().await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let provider = wallet.provider().cloned();
    let no_funds_wallet = WalletUnlocked::new_random(provider);
    let contract_instance = MyContract::new(contract_id, no_funds_wallet.clone());

    let contract_methods = contract_instance.methods();

    let call_handler_1 = contract_methods.get(1, 2);
    let call_handler_2 = contract_methods.get(3, 4);

    let mut multi_call_handler = CallHandler::new_multi_call(no_funds_wallet)
        .add_call(call_handler_1)
        .add_call(call_handler_2);

    let value: (u64, u64) = multi_call_handler
        .simulate(Execution::StateReadOnly)
        .await?
        .value;

    assert_eq!(value, (3, 7));

    Ok(())
}

#[tokio::test]
async fn contract_call_with_non_zero_base_asset_id_and_tip() -> Result<()> {
    use fuels::{prelude::*, tx::ConsensusParameters};

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let asset_id = AssetId::new([1; 32]);

    let mut consensus_parameters = ConsensusParameters::default();
    consensus_parameters.set_base_asset_id(asset_id);

    let config = ChainConfig {
        consensus_parameters,
        ..Default::default()
    };

    let asset_base = AssetConfig {
        id: asset_id,
        num_coins: 1,
        coin_amount: 10_000,
    };

    let wallet_config = WalletsConfig::new_multiple_assets(1, vec![asset_base]);
    let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, Some(config)).await?;
    let wallet = wallets.first().expect("has wallet");

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_tip(10))
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn max_fee_estimation_respects_tolerance() -> Result<()> {
    use fuels::prelude::*;

    let mut call_wallet = WalletUnlocked::new_random(None);

    let call_coins = setup_single_asset_coins(call_wallet.address(), AssetId::BASE, 1000, 1);

    let mut deploy_wallet = WalletUnlocked::new_random(None);
    let deploy_coins =
        setup_single_asset_coins(deploy_wallet.address(), AssetId::BASE, 1, 1_000_000);

    let provider =
        setup_test_provider([call_coins, deploy_coins].concat(), vec![], None, None).await?;

    call_wallet.set_provider(provider.clone());
    deploy_wallet.set_provider(provider.clone());

    setup_program_test!(
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            wallet = "deploy_wallet",
            contract = "MyContract",
            random_salt = false,
        )
    );
    let contract_instance = contract_instance.with_account(call_wallet.clone());

    let max_fee_from_tx = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            let builder = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap();

            assert_eq!(
                builder.max_fee_estimation_tolerance, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
                "Expected pre-set tolerance"
            );

            builder
                .with_max_fee_estimation_tolerance(tolerance)
                .build(&provider)
                .await
                .unwrap()
                .max_fee()
                .unwrap()
        }
    };

    let max_fee_from_builder = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let provider = provider.clone();
        async move {
            contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance)
                .estimate_max_fee(&provider)
                .await
                .unwrap()
        }
    };

    let base_amount_in_inputs = |tolerance: f32| {
        let contract_instance = contract_instance.clone();
        let call_wallet = &call_wallet;
        async move {
            let mut tb = contract_instance
                .methods()
                .initialize_counter(42)
                .transaction_builder()
                .await
                .unwrap()
                .with_max_fee_estimation_tolerance(tolerance);

            call_wallet.adjust_for_fee(&mut tb, 0).await.unwrap();
            tb.inputs
                .iter()
                .filter_map(|input: &Input| match input {
                    Input::ResourceSigned { resource }
                        if resource.coin_asset_id().unwrap() == AssetId::BASE =>
                    {
                        Some(resource.amount())
                    }
                    _ => None,
                })
                .sum::<u64>()
        }
    };

    let no_increase_max_fee = max_fee_from_tx(0.0).await;
    let increased_max_fee = max_fee_from_tx(2.00).await;

    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let no_increase_max_fee = max_fee_from_builder(0.0).await;
    let increased_max_fee = max_fee_from_builder(2.00).await;
    assert_eq!(
        increased_max_fee as f64 / no_increase_max_fee as f64,
        1.00 + 2.00
    );

    let normal_base_asset = base_amount_in_inputs(0.0).await;
    let more_base_asset_due_to_bigger_tolerance = base_amount_in_inputs(5.00).await;
    assert!(more_base_asset_due_to_bigger_tolerance > normal_base_asset);

    Ok(())
}

#[tokio::test]
async fn blob_contract_deployment() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
    ));

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract_size = std::fs::metadata(contract_binary)
        .expect("contract file not found")
        .len();

    assert!(
         contract_size > 150_000,
         "the testnet size limit was around 100kB, we want a contract bigger than that to reflect prod (current: {contract_size}B)"
     );

    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::new(Some(2), None, None), None, None)
            .await?;

    let provider = wallets[0].provider().unwrap().clone();

    let consensus_parameters = provider.consensus_parameters().await?;

    let contract_max_size = consensus_parameters.contract_params().contract_max_size();
    assert!(
         contract_size > contract_max_size,
         "this test should ideally be run with a contract bigger than the max contract size ({contract_max_size}B) so that we know deployment couldn't have happened without blobs"
     );

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100_000)?
        .deploy_if_not_exists(&wallets[0], TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallets[0].clone());

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn regular_contract_can_be_deployed() -> Result<()> {
    // given
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/contract_test"
        )),
    );

    let contract_binary = "sway/contracts/contract_test/out/release/contract_test.bin";

    // when
    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    // then
    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance
        .methods()
        .get_counter()
        .call()
        .await?
        .value;

    assert_eq!(response, 0);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_be_deployed_directly() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);

    let response = contract_instance.methods().something().call().await?.value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn unuploaded_loader_can_upload_blobs_separately_then_deploy() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    let blob_ids = contract.blob_ids();

    // if this were an example for the user we'd just call `deploy` on the contract above
    // this way we are testing that the blobs were really deployed above, otherwise the following
    // would fail
    let contract_id = Contract::loader_from_blob_ids(
        blob_ids.to_vec(),
        contract.salt(),
        contract.storage_slots().to_vec(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_blob_already_uploaded_not_an_issue() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "MyContract",
            project = "e2e/sway/contracts/huge_contract"
        )),
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";
    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .convert_to_loader(1024)?;

    // this will upload blobs
    contract
        .clone()
        .upload_blobs(&wallet, TxPolicies::default())
        .await?;

    // this will try to upload the blobs but skip upon encountering an error
    let contract_id = contract
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_instance = MyContract::new(contract_id, wallet);
    let response = contract_instance.methods().something().call().await?.value;
    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";

    let proxy_id = Contract::load_from(contract_binary, LoadConfiguration::default())?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .something()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 1001);

    Ok(())
}

#[tokio::test]
async fn loader_storage_works_via_proxy() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;

    abigen!(
        Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/huge_contract/out/release/huge_contract-abi.json"
        ),
        Contract(
            name = "MyProxy",
            abi = "e2e/sway/contracts/proxy/out/release/proxy-abi.json"
        )
    );

    let contract_binary = "sway/contracts/huge_contract/out/release/huge_contract.bin";

    let contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;
    let contract_storage_slots = contract.storage_slots().to_vec();

    let contract_id = contract
        .convert_to_loader(100)?
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let contract_binary = "sway/contracts/proxy/out/release/proxy.bin";
    let proxy_contract = Contract::load_from(contract_binary, LoadConfiguration::default())?;

    let combined_storage_slots = [&contract_storage_slots, proxy_contract.storage_slots()].concat();

    let proxy_id = proxy_contract
        .with_storage_slots(combined_storage_slots)
        .deploy_if_not_exists(&wallet, TxPolicies::default())
        .await?;

    let proxy = MyProxy::new(proxy_id, wallet.clone());
    proxy
        .methods()
        .set_target_contract(contract_id.clone())
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?
        .value;

    assert_eq!(response, 42);

    let _res = proxy
        .methods()
        .write_some_u64(36)
        .with_contract_ids(&[contract_id.clone()])
        .call()
        .await?;

    let response = proxy
        .methods()
        .read_some_u64()
        .with_contract_ids(&[contract_id])
        .call()
        .await?
        .value;

    assert_eq!(response, 36);

    Ok(())
}\n```

Increasing the block height

You can use produce_blocks to help achieve an arbitrary block height; this is useful when you want to do any testing regarding transaction maturity.

Note: For the produce_blocks API to work, it is imperative to have manual_blocks_enabled = true in the config for the running node. See example below.

```rust\nuse std::{ops::Add, path::Path};

use chrono::{DateTime, Duration, TimeZone, Utc};
use fuel_asm::RegId;
use fuel_tx::Witness;
use fuels::{
    accounts::{impersonated_account::ImpersonatedAccount, Account},
    client::{PageDirection, PaginationRequest},
    prelude::*,
    tx::Receipt,
    types::{
        coin_type::CoinType,
        message::Message,
        transaction_builders::{BuildableTransaction, ScriptTransactionBuilder},
        tx_status::TxStatus,
        Bits256,
    },
};

#[tokio::test]
async fn test_provider_launch_and_connect() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let mut wallet = WalletUnlocked::new_random(None);

    let coins = setup_single_asset_coins(
        wallet.address(),
        AssetId::zeroed(),
        DEFAULT_NUM_COINS,
        DEFAULT_COIN_AMOUNT,
    );
    let provider = setup_test_provider(coins, vec![], None, None).await?;
    wallet.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance_connected = MyContract::new(contract_id.clone(), wallet.clone());

    let response = contract_instance_connected
        .methods()
        .initialize_counter(42)
        .call()
        .await?;
    assert_eq!(42, response.value);

    wallet.set_provider(provider);
    let contract_instance_launched = MyContract::new(contract_id, wallet);

    let response = contract_instance_launched
        .methods()
        .increment_counter(10)
        .call()
        .await?;
    assert_eq!(52, response.value);
    Ok(())
}

#[tokio::test]
async fn test_network_error() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let mut wallet = WalletUnlocked::new_random(None);

    let node_config = NodeConfig::default();
    let chain_config = ChainConfig::default();
    let state_config = StateConfig::default();
    let service = FuelService::start(node_config, chain_config, state_config).await?;
    let provider = Provider::connect(service.bound_address().to_string()).await?;

    wallet.set_provider(provider);

    // Simulate an unreachable node
    service.stop().await.unwrap();

    let response = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await;

    assert!(matches!(response, Err(Error::Provider(_))));
    Ok(())
}

#[tokio::test]
async fn test_input_message() -> Result<()> {
    let compare_messages =
        |messages_from_provider: Vec<Message>, used_messages: Vec<Message>| -> bool {
            std::iter::zip(&used_messages, &messages_from_provider).all(|(a, b)| {
                a.sender == b.sender
                    && a.recipient == b.recipient
                    && a.nonce == b.nonce
                    && a.amount == b.amount
            })
        };

    let mut wallet = WalletUnlocked::new_random(None);

    // coin to pay transaction fee
    let coins =
        setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, DEFAULT_COIN_AMOUNT);

    let messages = vec![setup_single_message(
        &Bech32Address::default(),
        wallet.address(),
        DEFAULT_COIN_AMOUNT,
        0.into(),
        vec![1, 2],
    )];

    let provider = setup_test_provider(coins, messages.clone(), None, None).await?;
    wallet.set_provider(provider);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let spendable_messages = wallet.get_messages().await?;

    assert!(compare_messages(spendable_messages, messages));

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn test_input_message_pays_fee() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);

    let messages = setup_single_message(
        &Bech32Address {
            hrp: "".to_string(),
            hash: Default::default(),
        },
        wallet.address(),
        DEFAULT_COIN_AMOUNT,
        0.into(),
        vec![],
    );

    let provider = setup_test_provider(vec![], vec![messages], None, None).await?;
    let consensus_parameters = provider.consensus_parameters().await?;
    let base_asset_id = consensus_parameters.base_asset_id();
    wallet.set_provider(provider);

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let balance = wallet.get_asset_balance(base_asset_id).await?;
    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 2;
    assert_eq!(balance, DEFAULT_COIN_AMOUNT - expected_fee);

    Ok(())
}

#[tokio::test]
async fn can_increase_block_height() -> Result<()> {
    // ANCHOR: use_produce_blocks_to_increase_block_height
    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
    let wallet = &wallets[0];
    let provider = wallet.try_provider()?;

    assert_eq!(provider.latest_block_height().await?, 0u32);

    provider.produce_blocks(3, None).await?;

    assert_eq!(provider.latest_block_height().await?, 3u32);
    // ANCHOR_END: use_produce_blocks_to_increase_block_height
    Ok(())
}

// debug builds are slower (20x for `fuel-core-lib`, 4x for a release-fuel-core-binary), makes for
// flaky tests
#[cfg(not(feature = "fuel-core-lib"))]
#[tokio::test]
async fn can_set_custom_block_time() -> Result<()> {
    // ANCHOR: use_produce_blocks_custom_time
    let block_time = 20u32; // seconds
    let config = NodeConfig {
        // This is how you specify the time between blocks
        block_production: Trigger::Interval {
            block_time: std::time::Duration::from_secs(block_time.into()),
        },
        ..NodeConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::default(), Some(config), None)
            .await?;
    let wallet = &wallets[0];
    let provider = wallet.try_provider()?;

    assert_eq!(provider.latest_block_height().await?, 0u32);
    let origin_block_time = provider.latest_block_time().await?.unwrap();
    let blocks_to_produce = 3;

    provider.produce_blocks(blocks_to_produce, None).await?;
    assert_eq!(provider.latest_block_height().await?, blocks_to_produce);
    let expected_latest_block_time = origin_block_time
        .checked_add_signed(Duration::try_seconds((blocks_to_produce * block_time) as i64).unwrap())
        .unwrap();
    assert_eq!(
        provider.latest_block_time().await?.unwrap(),
        expected_latest_block_time
    );
    // ANCHOR_END: use_produce_blocks_custom_time

    let req = PaginationRequest {
        cursor: None,
        results: 10,
        direction: PageDirection::Forward,
    };
    let blocks: Vec<fuels::types::block::Block> = provider.get_blocks(req).await?.results;

    assert_eq!(blocks[1].header.time.unwrap().timestamp(), 20);
    assert_eq!(blocks[2].header.time.unwrap().timestamp(), 40);
    assert_eq!(blocks[3].header.time.unwrap().timestamp(), 60);
    Ok(())
}

#[tokio::test]
async fn can_retrieve_latest_block_time() -> Result<()> {
    let provider = setup_test_provider(vec![], vec![], None, None).await?;
    let since_epoch = 1676039910;

    let latest_timestamp = Utc.timestamp_opt(since_epoch, 0).unwrap();
    provider.produce_blocks(1, Some(latest_timestamp)).await?;

    assert_eq!(
        provider.latest_block_time().await?.unwrap(),
        latest_timestamp
    );

    Ok(())
}

#[tokio::test]
async fn contract_deployment_respects_maturity() -> Result<()> {
    abigen!(Contract(name="MyContract", abi="e2e/sway/contracts/transaction_block_height/out/release/transaction_block_height-abi.json"));

    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
    let wallet = &wallets[0];
    let provider = wallet.try_provider()?;

    let deploy_w_maturity = |maturity| {
        Contract::load_from(
            "sway/contracts/transaction_block_height/out/release/transaction_block_height.bin",
            LoadConfiguration::default(),
        )
        .map(|loaded_contract| {
            loaded_contract
                .deploy_if_not_exists(wallet, TxPolicies::default().with_maturity(maturity))
        })
    };

    let err = deploy_w_maturity(1)?.await.expect_err(
        "should not deploy contract since block height `0` is less than the requested maturity `1`",
    );

    let Error::Provider(s) = err else {
        panic!("expected `Validation`, got: `{err}`");
    };
    assert!(s.contains("TransactionMaturity"));

    provider.produce_blocks(1, None).await?;
    deploy_w_maturity(1)?
        .await
        .expect("Should deploy contract since maturity `1` is <= than the block height `1`");

    Ok(())
}

#[tokio::test]
async fn test_gas_forwarded_defaults_to_tx_limit() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // The gas used by the script to call a contract and forward remaining gas limit.
    let gas_used_by_script = 247;
    let gas_limit = 225_883;
    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit))
        .call()
        .await?;

    let gas_forwarded = response
        .receipts
        .iter()
        .find(|r| matches!(r, Receipt::Call { .. }))
        .unwrap()
        .gas()
        .unwrap();

    assert_eq!(gas_limit, gas_forwarded + gas_used_by_script);

    Ok(())
}

#[tokio::test]
async fn test_amount_and_asset_forwarding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TokenContract",
            project = "e2e/sway/contracts/token_ops"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TokenContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_id = contract_instance.contract_id();
    let contract_methods = contract_instance.methods();
    let asset_id = contract_id.asset_id(&Bits256::zeroed());

    let mut balance_response = contract_methods
        .get_balance(contract_id, asset_id)
        .call()
        .await?;
    assert_eq!(balance_response.value, 0);

    contract_methods.mint_coins(5_000_000).call().await?;

    balance_response = contract_methods
        .get_balance(contract_id, asset_id)
        .call()
        .await?;
    assert_eq!(balance_response.value, 5_000_000);

    let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
    // Forward 1_000_000 coin amount of base asset_id
    // this is a big number for checking that amount can be a u64
    let call_params = CallParameters::default().with_amount(1_000_000);

    let response = contract_methods
        .get_msg_amount()
        .with_tx_policies(tx_policies)
        .call_params(call_params)?
        .call()
        .await?;

    assert_eq!(response.value, 1_000_000);

    let call_response = response
        .receipts
        .iter()
        .find(|&r| matches!(r, Receipt::Call { .. }));

    assert!(call_response.is_some());

    assert_eq!(call_response.unwrap().amount().unwrap(), 1_000_000);
    assert_eq!(
        call_response.unwrap().asset_id().unwrap(),
        &AssetId::zeroed()
    );

    let address = wallet.address();

    // withdraw some tokens to wallet
    contract_methods
        .transfer(1_000_000, asset_id, address.into())
        .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
        .call()
        .await?;

    let asset_id = AssetId::from(*contract_id.hash());
    let call_params = CallParameters::default()
        .with_amount(0)
        .with_asset_id(asset_id);
    let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);

    let response = contract_methods
        .get_msg_amount()
        .with_tx_policies(tx_policies)
        .call_params(call_params)?
        .call()
        .await?;

    assert_eq!(response.value, 0);

    let call_response = response
        .receipts
        .iter()
        .find(|&r| matches!(r, Receipt::Call { .. }));

    assert!(call_response.is_some());

    assert_eq!(call_response.unwrap().amount().unwrap(), 0);
    assert_eq!(
        call_response.unwrap().asset_id().unwrap(),
        &AssetId::from(*contract_id.hash())
    );

    Ok(())
}

#[tokio::test]
async fn test_gas_errors() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let number_of_coins = 1;
    let amount_per_coin = 1_000_000;
    let coins = setup_single_asset_coins(
        wallet.address(),
        AssetId::zeroed(),
        number_of_coins,
        amount_per_coin,
    );

    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Test running out of gas. Gas price as `None` will be 0.
    let gas_limit = 100;
    let contract_instance_call = contract_instance
        .methods()
        .initialize_counter(42) // Build the ABI call
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit));

    //  Test that the call will use more gas than the gas limit
    let gas_used = contract_instance_call
        .estimate_transaction_cost(None, None)
        .await?
        .gas_used;
    assert!(gas_used > gas_limit);

    let response = contract_instance_call
        .call()
        .await
        .expect_err("should error");

    let expected = "transaction reverted: OutOfGas";
    assert!(response.to_string().starts_with(expected));

    // Test for insufficient base asset amount to pay for the transaction fee
    let response = contract_instance
        .methods()
        .initialize_counter(42) // Build the ABI call
        .with_tx_policies(TxPolicies::default().with_tip(100_000_000_000))
        .call()
        .await
        .expect_err("should error");

    let expected = "Response errors; Validity(InsufficientFeeAmount";
    assert!(response.to_string().contains(expected));

    Ok(())
}

#[tokio::test]
async fn test_call_param_gas_errors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Transaction gas_limit is sufficient, call gas_forwarded is too small
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(446000))
        .call_params(CallParameters::default().with_gas_forwarded(1))?
        .call()
        .await
        .expect_err("should error");

    let expected = "transaction reverted: OutOfGas";
    assert!(response.to_string().starts_with(expected));

    // Call params gas_forwarded exceeds transaction limit
    let response = contract_methods
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(1))
        .call_params(CallParameters::default().with_gas_forwarded(1_000))?
        .call()
        .await
        .expect_err("should error");

    assert!(response.to_string().contains(expected));
    Ok(())
}

#[tokio::test]
async fn test_get_gas_used() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let gas_used = contract_instance
        .methods()
        .initialize_counter(42)
        .call()
        .await?
        .gas_used;

    assert!(gas_used > 0);
    Ok(())
}

#[tokio::test]
async fn test_parse_block_time() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let asset_id = AssetId::zeroed();
    let coins = setup_single_asset_coins(wallet.address(), asset_id, 1, DEFAULT_COIN_AMOUNT);
    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider);
    let tx_policies = TxPolicies::default().with_script_gas_limit(2000);

    let wallet_2 = WalletUnlocked::new_random(None).lock();
    let (tx_id, _) = wallet
        .transfer(wallet_2.address(), 100, asset_id, tx_policies)
        .await?;

    let tx_response = wallet
        .try_provider()?
        .get_transaction_by_id(&tx_id)
        .await?
        .unwrap();
    assert!(tx_response.time.is_some());

    let block = wallet
        .try_provider()?
        .block_by_height(tx_response.block_height.unwrap())
        .await?
        .unwrap();
    assert!(block.header.time.is_some());

    Ok(())
}

#[tokio::test]
async fn test_get_spendable_with_exclusion() -> Result<()> {
    let coin_amount_1 = 1000;
    let coin_amount_2 = 500;

    let mut wallet = WalletUnlocked::new_random(None);
    let address = wallet.address();

    let coins = [coin_amount_1, coin_amount_2]
        .into_iter()
        .flat_map(|amount| setup_single_asset_coins(address, AssetId::zeroed(), 1, amount))
        .collect::<Vec<_>>();

    let message_amount = 200;
    let message = given_a_message(address.clone(), message_amount);

    let coin_1_utxo_id = coins[0].utxo_id;
    let coin_2_utxo_id = coins[1].utxo_id;

    let message_nonce = message.nonce;

    let provider = setup_test_provider(coins, vec![message], None, None).await?;

    wallet.set_provider(provider.clone());

    let requested_amount = coin_amount_1 + coin_amount_2 + message_amount;
    let consensus_parameters = provider.consensus_parameters().await?;
    {
        let resources = wallet
            .get_spendable_resources(
                *consensus_parameters.base_asset_id(),
                requested_amount,
                None,
            )
            .await
            .unwrap();
        assert_eq!(resources.len(), 3);
    }

    {
        let filter = ResourceFilter {
            from: wallet.address().clone(),
            amount: coin_amount_1,
            excluded_utxos: vec![coin_2_utxo_id],
            excluded_message_nonces: vec![message_nonce],
            ..Default::default()
        };
        let resources = provider.get_spendable_resources(filter).await.unwrap();

        match resources.as_slice() {
            [CoinType::Coin(coin)] => {
                assert_eq!(coin.utxo_id, coin_1_utxo_id);
            }
            _ => {
                panic!("This shouldn't happen!")
            }
        }
    }

    Ok(())
}

fn given_a_message(address: Bech32Address, message_amount: u64) -> Message {
    setup_single_message(
        &Bech32Address::default(),
        &address,
        message_amount,
        0.into(),
        vec![],
    )
}

fn convert_to_datetime(timestamp: u64) -> DateTime<Utc> {
    let unix = tai64::Tai64(timestamp).to_unix();
    DateTime::from_timestamp(unix, 0).unwrap()
}

/// This test is here in addition to `can_set_custom_block_time` because even though this test
/// passed, the Sway `timestamp` function didn't take into account the block time change. This
/// was fixed and this test is here to demonstrate the fix.
#[tokio::test]
async fn test_sway_timestamp() -> Result<()> {
    let block_time = 1u32; // seconds
    let provider_config = NodeConfig {
        block_production: Trigger::Interval {
            block_time: std::time::Duration::from_secs(block_time.into()),
        },
        ..NodeConfig::default()
    };
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(Some(1), Some(1), Some(100)),
        Some(provider_config),
        None,
    )
    .await?;
    let wallet = wallets.pop().unwrap();
    let provider = wallet.try_provider()?;

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/block_timestamp"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let origin_timestamp = provider.latest_block_time().await?.unwrap();
    let methods = contract_instance.methods();

    let response = methods.return_timestamp().call().await?;
    let mut expected_datetime =
        origin_timestamp.add(Duration::try_seconds(block_time as i64).unwrap());
    assert_eq!(convert_to_datetime(response.value), expected_datetime);

    let blocks_to_produce = 600;
    provider.produce_blocks(blocks_to_produce, None).await?;

    let response = methods.return_timestamp().call().await?;

    // `produce_blocks` call
    expected_datetime = expected_datetime
        .add(Duration::try_seconds((block_time * blocks_to_produce) as i64).unwrap());
    // method call
    expected_datetime = expected_datetime.add(Duration::try_seconds(block_time as i64).unwrap());

    assert_eq!(convert_to_datetime(response.value), expected_datetime);
    assert_eq!(
        provider.latest_block_time().await?.unwrap(),
        expected_datetime
    );
    Ok(())
}

#[cfg(feature = "coin-cache")]
async fn create_transfer(
    wallet: &WalletUnlocked,
    amount: u64,
    to: &Bech32Address,
) -> Result<ScriptTransaction> {
    let asset_id = AssetId::zeroed();
    let inputs = wallet
        .get_asset_inputs_for_amount(asset_id, amount, None)
        .await?;
    let outputs = wallet.get_asset_outputs_for_amount(to, asset_id, amount);

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
    tb.add_signer(wallet.clone())?;

    wallet.adjust_for_fee(&mut tb, amount).await?;

    tb.build(wallet.try_provider()?).await
}

#[cfg(feature = "coin-cache")]
#[tokio::test]
async fn transactions_with_the_same_utxo() -> Result<()> {
    use fuels::types::errors::transaction;

    let wallet_1 = launch_provider_and_get_wallet().await?;
    let provider = wallet_1.provider().unwrap();
    let wallet_2 = WalletUnlocked::new_random(Some(provider.clone()));

    let tx_1 = create_transfer(&wallet_1, 100, wallet_2.address()).await?;
    let tx_2 = create_transfer(&wallet_1, 101, wallet_2.address()).await?;

    let _tx_id = provider.send_transaction(tx_1).await?;
    let res = provider.send_transaction(tx_2).await;

    let err = res.expect_err("is error");

    assert!(matches!(
        err,
        Error::Transaction(transaction::Reason::Validation(..))
    ));
    assert!(err
        .to_string()
        .contains("was submitted recently in a transaction "));

    Ok(())
}

#[cfg(feature = "coin-cache")]
#[tokio::test]
async fn test_caching() -> Result<()> {
    let amount = 1000;
    let num_coins = 10;
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(Some(1), Some(num_coins), Some(amount)),
        Some(NodeConfig::default()),
        None,
    )
    .await?;
    let wallet_1 = wallets.pop().unwrap();
    let provider = wallet_1.provider().unwrap();
    let wallet_2 = WalletUnlocked::new_random(Some(provider.clone()));

    // Consecutively send transfer txs. Without caching, the txs will
    // end up trying to use the same input coins because 'get_spendable_coins()'
    // won't filter out recently used coins.
    let mut tx_ids = vec![];
    for _ in 0..10 {
        let tx = create_transfer(&wallet_1, 100, wallet_2.address()).await?;
        let tx_id = provider.send_transaction(tx).await?;
        tx_ids.push(tx_id);
    }

    provider.produce_blocks(10, None).await?;

    // Confirm all txs are settled
    for tx_id in tx_ids {
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));
    }

    // Verify the transfers were successful
    assert_eq!(wallet_2.get_asset_balance(&AssetId::zeroed()).await?, 1000);

    Ok(())
}

#[cfg(feature = "coin-cache")]
async fn create_revert_tx(wallet: &WalletUnlocked) -> Result<ScriptTransaction> {
    let script = std::fs::read("sway/scripts/reverting/out/release/reverting.bin")?;

    let amount = 1;
    let asset_id = AssetId::zeroed();
    let inputs = wallet
        .get_asset_inputs_for_amount(asset_id, amount, None)
        .await?;
    let outputs = wallet.get_asset_outputs_for_amount(&Bech32Address::default(), asset_id, amount);

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default())
        .with_script(script);
    tb.add_signer(wallet.clone())?;

    wallet.adjust_for_fee(&mut tb, amount).await?;

    tb.build(wallet.try_provider()?).await
}

#[cfg(feature = "coin-cache")]
#[tokio::test]
async fn test_cache_invalidation_on_await() -> Result<()> {
    let block_time = 1u32;
    let provider_config = NodeConfig {
        block_production: Trigger::Interval {
            block_time: std::time::Duration::from_secs(block_time.into()),
        },
        ..NodeConfig::default()
    };

    // create wallet with 1 coin so that the cache prevents further
    // spending unless the coin is invalidated from the cache
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(Some(1), Some(1), Some(100)),
        Some(provider_config),
        None,
    )
    .await?;
    let wallet = wallets.pop().unwrap();

    let provider = wallet.provider().unwrap();
    let tx = create_revert_tx(&wallet).await?;

    // Pause time so that the cache doesn't invalidate items based on TTL
    tokio::time::pause();

    // tx inputs should be cached and then invalidated due to the tx failing
    let tx_status = provider.send_transaction_and_await_commit(tx).await?;

    assert!(matches!(tx_status, TxStatus::Revert { .. }));

    let consensus_parameters = provider.consensus_parameters().await?;
    let coins = wallet
        .get_spendable_resources(*consensus_parameters.base_asset_id(), 1, None)
        .await?;
    assert_eq!(coins.len(), 1);

    Ok(())
}

#[tokio::test]
async fn can_fetch_mint_transactions() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let provider = wallet.try_provider()?;

    let transactions = provider
        .get_transactions(PaginationRequest {
            cursor: None,
            results: 100,
            direction: PageDirection::Forward,
        })
        .await?
        .results;

    // TODO: remove once (fuels-rs#1093)[https://github.com/FuelLabs/fuels-rs/issues/1093] is in
    // until then the type is explicitly mentioned to check that we're reexporting it through fuels
    let _: ::fuels::types::transaction::MintTransaction = transactions
        .into_iter()
        .find_map(|tx| match tx.transaction {
            TransactionType::Mint(tx) => Some(tx),
            _ => None,
        })
        .expect("Should have had at least one mint transaction");

    Ok(())
}

#[tokio::test]
async fn test_build_with_provider() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?;

    let receiver = WalletUnlocked::new_random(Some(provider.clone()));

    let consensus_parameters = provider.consensus_parameters().await?;
    let inputs = wallet
        .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), 100, None)
        .await?;
    let outputs = wallet.get_asset_outputs_for_amount(
        receiver.address(),
        *consensus_parameters.base_asset_id(),
        100,
    );

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(provider).await?;

    provider.send_transaction_and_await_commit(tx).await?;

    let receiver_balance = receiver
        .get_asset_balance(consensus_parameters.base_asset_id())
        .await?;

    assert_eq!(receiver_balance, 100);

    Ok(())
}

#[tokio::test]
async fn can_produce_blocks_with_trig_never() -> Result<()> {
    let config = NodeConfig {
        block_production: Trigger::Never,
        ..NodeConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::default(), Some(config), None)
            .await?;
    let wallet = &wallets[0];
    let provider = wallet.try_provider()?;

    let consensus_parameters = provider.consensus_parameters().await?;
    let inputs = wallet
        .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), 100, None)
        .await?;
    let outputs = wallet.get_asset_outputs_for_amount(
        &Bech32Address::default(),
        *consensus_parameters.base_asset_id(),
        100,
    );

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
    tb.add_signer(wallet.clone())?;
    let tx = tb.build(provider).await?;
    let tx_id = tx.id(consensus_parameters.chain_id());

    provider.send_transaction(tx).await?;
    provider.produce_blocks(1, None).await?;

    tokio::time::sleep(std::time::Duration::from_millis(500)).await;
    let status = provider.tx_status(&tx_id).await?;
    assert!(matches!(status, TxStatus::Success { .. }));

    Ok(())
}

#[tokio::test]
async fn can_upload_executor_and_trigger_upgrade() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);

    // Need more coins to avoid "not enough coins to fit the target"
    let num_coins = 100;
    let coins = setup_single_asset_coins(
        wallet.address(),
        AssetId::zeroed(),
        num_coins,
        DEFAULT_COIN_AMOUNT,
    );

    let mut chain_config = ChainConfig::local_testnet();
    chain_config
        .consensus_parameters
        .set_privileged_address(wallet.address().into());

    let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
    wallet.set_provider(provider.clone());

    // This is downloaded over in `build.rs`
    let executor = std::fs::read(Path::new(env!("OUT_DIR")).join("fuel-core-wasm-executor.wasm"))?;

    let subsection_size = 65536;
    let subsections = UploadSubsection::split_bytecode(&executor, subsection_size).unwrap();

    let root = subsections[0].root;
    for subsection in subsections {
        let mut builder =
            UploadTransactionBuilder::prepare_subsection_upload(subsection, TxPolicies::default());
        wallet.add_witnesses(&mut builder)?;
        wallet.adjust_for_fee(&mut builder, 0).await?;
        let tx = builder.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;
    }

    let mut builder =
        UpgradeTransactionBuilder::prepare_state_transition_upgrade(root, TxPolicies::default());
    wallet.add_witnesses(&mut builder)?;
    wallet.adjust_for_fee(&mut builder, 0).await?;
    let tx = builder.build(provider.clone()).await?;

    provider.send_transaction(tx).await?;

    Ok(())
}

#[tokio::test]
async fn tx_respects_policies() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let tip = 22;
    let witness_limit = 1000;
    let maturity = 4;
    let max_fee = 10_000;
    let script_gas_limit = 3000;
    let tx_policies = TxPolicies::new(
        Some(tip),
        Some(witness_limit),
        Some(maturity),
        Some(max_fee),
        Some(script_gas_limit),
    );

    // advance the block height to ensure the maturity is respected
    let provider = wallet.try_provider()?;
    provider.produce_blocks(4, None).await?;

    // trigger a transaction that contains script code to verify
    // that policies precede estimated values
    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    let tx_response = provider
        .get_transaction_by_id(&response.tx_id.unwrap())
        .await?
        .expect("tx should exist");
    let script = match tx_response.transaction {
        TransactionType::Script(tx) => tx,
        _ => panic!("expected script transaction"),
    };

    assert_eq!(script.maturity(), maturity as u32);
    assert_eq!(script.tip().unwrap(), tip);
    assert_eq!(script.witness_limit().unwrap(), witness_limit);
    assert_eq!(script.max_fee().unwrap(), max_fee);
    assert_eq!(script.gas_limit(), script_gas_limit);

    Ok(())
}

#[tokio::test]
#[ignore] //TODO: https://github.com/FuelLabs/fuels-rs/issues/1581
async fn can_setup_static_gas_price() -> Result<()> {
    let expected_gas_price = 474;
    let node_config = NodeConfig {
        starting_gas_price: expected_gas_price,
        ..Default::default()
    };
    let provider = setup_test_provider(vec![], vec![], Some(node_config), None).await?;

    let gas_price = provider.estimate_gas_price(0).await?.gas_price;

    let da_cost = 1000;
    assert_eq!(gas_price, da_cost + expected_gas_price);

    Ok(())
}

#[tokio::test]
async fn tx_with_witness_data() -> Result<()> {
    use fuel_asm::{op, GTFArgs};

    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?;

    let receiver = WalletUnlocked::new_random(Some(provider.clone()));

    let consensus_parameters = provider.consensus_parameters().await?;
    let inputs = wallet
        .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), 10000, None)
        .await?;
    let outputs = wallet.get_asset_outputs_for_amount(
        receiver.address(),
        *consensus_parameters.base_asset_id(),
        1,
    );

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
    tb.add_signer(wallet.clone())?;

    // we test that the witness data wasn't tempered with during the build (gas estimation) process
    // if the witness data is tempered with, the estimation will be off and the transaction
    // will error out with `OutOfGas`
    let script: Vec<u8> = vec![
        // load witness data into register 0x10
        op::gtf(0x10, 0x00, GTFArgs::WitnessData.into()),
        op::lw(0x10, 0x10, 0x00),
        // load expected value into register 0x11
        op::movi(0x11, 0x0f),
        // load the offset of the revert instruction into register 0x12
        op::movi(0x12, 0x08),
        // compare the two values and jump to the revert instruction if they are not equal
        op::jne(0x10, 0x11, 0x12),
        // do some expensive operation so gas estimation is higher if comparison passes
        op::gtf(0x13, 0x01, GTFArgs::WitnessData.into()),
        op::gtf(0x14, 0x01, GTFArgs::WitnessDataLength.into()),
        op::aloc(0x14),
        op::eck1(RegId::HP, 0x13, 0x13),
        // return the witness data
        op::ret(0x10),
        op::rvrt(RegId::ZERO),
    ]
    .into_iter()
    .collect();
    tb.script = script;

    let expected_data = 15u64;
    let witness = Witness::from(expected_data.to_be_bytes().to_vec());
    tb.witnesses_mut().push(witness);

    let tx = tb
        .with_tx_policies(TxPolicies::default().with_witness_limit(1000))
        .build(provider)
        .await?;

    let status = provider.send_transaction_and_await_commit(tx).await?;

    match status {
        TxStatus::Success { receipts } => {
            let ret: u64 = receipts
                .into_iter()
                .find_map(|receipt| match receipt {
                    Receipt::Return { val, .. } => Some(val),
                    _ => None,
                })
                .expect("should have return value");

            assert_eq!(ret, expected_data);
        }
        _ => panic!("expected success status"),
    }

    Ok(())
}

#[tokio::test]
async fn contract_call_with_impersonation() -> Result<()> {
    let provider_config = NodeConfig {
        utxo_validation: false,
        ..NodeConfig::default()
    };
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(Some(1), Some(10), Some(1000)),
        Some(provider_config),
        None,
    )
    .await?;
    let wallet = wallets.pop().unwrap();
    let provider = wallet.try_provider()?;

    let impersonator = ImpersonatedAccount::new(wallet.address().clone(), Some(provider.clone()));

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, impersonator.clone());

    // The gas used by the script to call a contract and forward remaining gas limit.
    contract_instance
        .methods()
        .initialize_counter(42)
        .call()
        .await?;

    Ok(())
}

#[tokio::test]
async fn is_account_query_test() -> Result<()> {
    {
        let wallet = launch_provider_and_get_wallet().await?;
        let provider = wallet.provider().unwrap().clone();

        let blob = Blob::new(vec![1; 100]);
        let blob_id = blob.id();

        let is_account = provider.is_user_account(blob_id).await?;
        assert!(is_account);

        let mut tb = BlobTransactionBuilder::default().with_blob(blob);
        wallet.adjust_for_fee(&mut tb, 0).await?;
        wallet.add_witnesses(&mut tb)?;
        let tx = tb.build(provider.clone()).await?;

        provider
            .send_transaction_and_await_commit(tx)
            .await?
            .check(None)?;

        let is_account = provider.is_user_account(blob_id).await?;
        assert!(!is_account);
    }
    {
        let wallet = launch_provider_and_get_wallet().await?;
        let provider = wallet.provider().unwrap().clone();

        let contract = Contract::load_from(
            "sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?;
        let contract_id = contract.contract_id();

        let is_account = provider.is_user_account(*contract_id).await?;
        assert!(is_account);

        contract.deploy(&wallet, TxPolicies::default()).await?;

        let is_account = provider.is_user_account(*contract_id).await?;
        assert!(!is_account);
    }
    {
        let wallet = launch_provider_and_get_wallet().await?;
        let provider = wallet.provider().unwrap().clone();

        let mut tb = ScriptTransactionBuilder::default();
        wallet.adjust_for_fee(&mut tb, 0).await?;
        wallet.add_witnesses(&mut tb)?;
        let tx = tb.build(provider.clone()).await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let tx_id = tx.id(consensus_parameters.chain_id());
        let is_account = provider.is_user_account(tx_id).await?;
        assert!(is_account);

        provider
            .send_transaction_and_await_commit(tx)
            .await?
            .check(None)?;
        let is_account = provider.is_user_account(tx_id).await?;
        assert!(!is_account);
    }

    Ok(())
}\n```

You can also set a custom block time as the second, optional argument. Here is an example:

```rust\nuse std::{ops::Add, path::Path};

use chrono::{DateTime, Duration, TimeZone, Utc};
use fuel_asm::RegId;
use fuel_tx::Witness;
use fuels::{
    accounts::{impersonated_account::ImpersonatedAccount, Account},
    client::{PageDirection, PaginationRequest},
    prelude::*,
    tx::Receipt,
    types::{
        coin_type::CoinType,
        message::Message,
        transaction_builders::{BuildableTransaction, ScriptTransactionBuilder},
        tx_status::TxStatus,
        Bits256,
    },
};

#[tokio::test]
async fn test_provider_launch_and_connect() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let mut wallet = WalletUnlocked::new_random(None);

    let coins = setup_single_asset_coins(
        wallet.address(),
        AssetId::zeroed(),
        DEFAULT_NUM_COINS,
        DEFAULT_COIN_AMOUNT,
    );
    let provider = setup_test_provider(coins, vec![], None, None).await?;
    wallet.set_provider(provider.clone());

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance_connected = MyContract::new(contract_id.clone(), wallet.clone());

    let response = contract_instance_connected
        .methods()
        .initialize_counter(42)
        .call()
        .await?;
    assert_eq!(42, response.value);

    wallet.set_provider(provider);
    let contract_instance_launched = MyContract::new(contract_id, wallet);

    let response = contract_instance_launched
        .methods()
        .increment_counter(10)
        .call()
        .await?;
    assert_eq!(52, response.value);
    Ok(())
}

#[tokio::test]
async fn test_network_error() -> Result<()> {
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let mut wallet = WalletUnlocked::new_random(None);

    let node_config = NodeConfig::default();
    let chain_config = ChainConfig::default();
    let state_config = StateConfig::default();
    let service = FuelService::start(node_config, chain_config, state_config).await?;
    let provider = Provider::connect(service.bound_address().to_string()).await?;

    wallet.set_provider(provider);

    // Simulate an unreachable node
    service.stop().await.unwrap();

    let response = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await;

    assert!(matches!(response, Err(Error::Provider(_))));
    Ok(())
}

#[tokio::test]
async fn test_input_message() -> Result<()> {
    let compare_messages =
        |messages_from_provider: Vec<Message>, used_messages: Vec<Message>| -> bool {
            std::iter::zip(&used_messages, &messages_from_provider).all(|(a, b)| {
                a.sender == b.sender
                    && a.recipient == b.recipient
                    && a.nonce == b.nonce
                    && a.amount == b.amount
            })
        };

    let mut wallet = WalletUnlocked::new_random(None);

    // coin to pay transaction fee
    let coins =
        setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, DEFAULT_COIN_AMOUNT);

    let messages = vec![setup_single_message(
        &Bech32Address::default(),
        wallet.address(),
        DEFAULT_COIN_AMOUNT,
        0.into(),
        vec![1, 2],
    )];

    let provider = setup_test_provider(coins, messages.clone(), None, None).await?;
    wallet.set_provider(provider);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let spendable_messages = wallet.get_messages().await?;

    assert!(compare_messages(spendable_messages, messages));

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .call()
        .await?;

    assert_eq!(42, response.value);

    Ok(())
}

#[tokio::test]
async fn test_input_message_pays_fee() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);

    let messages = setup_single_message(
        &Bech32Address {
            hrp: "".to_string(),
            hash: Default::default(),
        },
        wallet.address(),
        DEFAULT_COIN_AMOUNT,
        0.into(),
        vec![],
    );

    let provider = setup_test_provider(vec![], vec![messages], None, None).await?;
    let consensus_parameters = provider.consensus_parameters().await?;
    let base_asset_id = consensus_parameters.base_asset_id();
    wallet.set_provider(provider);

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, wallet.clone());

    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .call()
        .await?;

    assert_eq!(42, response.value);

    let balance = wallet.get_asset_balance(base_asset_id).await?;
    // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394
    let expected_fee = 2;
    assert_eq!(balance, DEFAULT_COIN_AMOUNT - expected_fee);

    Ok(())
}

#[tokio::test]
async fn can_increase_block_height() -> Result<()> {
    // ANCHOR: use_produce_blocks_to_increase_block_height
    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
    let wallet = &wallets[0];
    let provider = wallet.try_provider()?;

    assert_eq!(provider.latest_block_height().await?, 0u32);

    provider.produce_blocks(3, None).await?;

    assert_eq!(provider.latest_block_height().await?, 3u32);
    // ANCHOR_END: use_produce_blocks_to_increase_block_height
    Ok(())
}

// debug builds are slower (20x for `fuel-core-lib`, 4x for a release-fuel-core-binary), makes for
// flaky tests
#[cfg(not(feature = "fuel-core-lib"))]
#[tokio::test]
async fn can_set_custom_block_time() -> Result<()> {
    // ANCHOR: use_produce_blocks_custom_time
    let block_time = 20u32; // seconds
    let config = NodeConfig {
        // This is how you specify the time between blocks
        block_production: Trigger::Interval {
            block_time: std::time::Duration::from_secs(block_time.into()),
        },
        ..NodeConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::default(), Some(config), None)
            .await?;
    let wallet = &wallets[0];
    let provider = wallet.try_provider()?;

    assert_eq!(provider.latest_block_height().await?, 0u32);
    let origin_block_time = provider.latest_block_time().await?.unwrap();
    let blocks_to_produce = 3;

    provider.produce_blocks(blocks_to_produce, None).await?;
    assert_eq!(provider.latest_block_height().await?, blocks_to_produce);
    let expected_latest_block_time = origin_block_time
        .checked_add_signed(Duration::try_seconds((blocks_to_produce * block_time) as i64).unwrap())
        .unwrap();
    assert_eq!(
        provider.latest_block_time().await?.unwrap(),
        expected_latest_block_time
    );
    // ANCHOR_END: use_produce_blocks_custom_time

    let req = PaginationRequest {
        cursor: None,
        results: 10,
        direction: PageDirection::Forward,
    };
    let blocks: Vec<fuels::types::block::Block> = provider.get_blocks(req).await?.results;

    assert_eq!(blocks[1].header.time.unwrap().timestamp(), 20);
    assert_eq!(blocks[2].header.time.unwrap().timestamp(), 40);
    assert_eq!(blocks[3].header.time.unwrap().timestamp(), 60);
    Ok(())
}

#[tokio::test]
async fn can_retrieve_latest_block_time() -> Result<()> {
    let provider = setup_test_provider(vec![], vec![], None, None).await?;
    let since_epoch = 1676039910;

    let latest_timestamp = Utc.timestamp_opt(since_epoch, 0).unwrap();
    provider.produce_blocks(1, Some(latest_timestamp)).await?;

    assert_eq!(
        provider.latest_block_time().await?.unwrap(),
        latest_timestamp
    );

    Ok(())
}

#[tokio::test]
async fn contract_deployment_respects_maturity() -> Result<()> {
    abigen!(Contract(name="MyContract", abi="e2e/sway/contracts/transaction_block_height/out/release/transaction_block_height-abi.json"));

    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
    let wallet = &wallets[0];
    let provider = wallet.try_provider()?;

    let deploy_w_maturity = |maturity| {
        Contract::load_from(
            "sway/contracts/transaction_block_height/out/release/transaction_block_height.bin",
            LoadConfiguration::default(),
        )
        .map(|loaded_contract| {
            loaded_contract
                .deploy_if_not_exists(wallet, TxPolicies::default().with_maturity(maturity))
        })
    };

    let err = deploy_w_maturity(1)?.await.expect_err(
        "should not deploy contract since block height `0` is less than the requested maturity `1`",
    );

    let Error::Provider(s) = err else {
        panic!("expected `Validation`, got: `{err}`");
    };
    assert!(s.contains("TransactionMaturity"));

    provider.produce_blocks(1, None).await?;
    deploy_w_maturity(1)?
        .await
        .expect("Should deploy contract since maturity `1` is <= than the block height `1`");

    Ok(())
}

#[tokio::test]
async fn test_gas_forwarded_defaults_to_tx_limit() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // The gas used by the script to call a contract and forward remaining gas limit.
    let gas_used_by_script = 247;
    let gas_limit = 225_883;
    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit))
        .call()
        .await?;

    let gas_forwarded = response
        .receipts
        .iter()
        .find(|r| matches!(r, Receipt::Call { .. }))
        .unwrap()
        .gas()
        .unwrap();

    assert_eq!(gas_limit, gas_forwarded + gas_used_by_script);

    Ok(())
}

#[tokio::test]
async fn test_amount_and_asset_forwarding() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TokenContract",
            project = "e2e/sway/contracts/token_ops"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TokenContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );
    let contract_id = contract_instance.contract_id();
    let contract_methods = contract_instance.methods();
    let asset_id = contract_id.asset_id(&Bits256::zeroed());

    let mut balance_response = contract_methods
        .get_balance(contract_id, asset_id)
        .call()
        .await?;
    assert_eq!(balance_response.value, 0);

    contract_methods.mint_coins(5_000_000).call().await?;

    balance_response = contract_methods
        .get_balance(contract_id, asset_id)
        .call()
        .await?;
    assert_eq!(balance_response.value, 5_000_000);

    let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
    // Forward 1_000_000 coin amount of base asset_id
    // this is a big number for checking that amount can be a u64
    let call_params = CallParameters::default().with_amount(1_000_000);

    let response = contract_methods
        .get_msg_amount()
        .with_tx_policies(tx_policies)
        .call_params(call_params)?
        .call()
        .await?;

    assert_eq!(response.value, 1_000_000);

    let call_response = response
        .receipts
        .iter()
        .find(|&r| matches!(r, Receipt::Call { .. }));

    assert!(call_response.is_some());

    assert_eq!(call_response.unwrap().amount().unwrap(), 1_000_000);
    assert_eq!(
        call_response.unwrap().asset_id().unwrap(),
        &AssetId::zeroed()
    );

    let address = wallet.address();

    // withdraw some tokens to wallet
    contract_methods
        .transfer(1_000_000, asset_id, address.into())
        .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
        .call()
        .await?;

    let asset_id = AssetId::from(*contract_id.hash());
    let call_params = CallParameters::default()
        .with_amount(0)
        .with_asset_id(asset_id);
    let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);

    let response = contract_methods
        .get_msg_amount()
        .with_tx_policies(tx_policies)
        .call_params(call_params)?
        .call()
        .await?;

    assert_eq!(response.value, 0);

    let call_response = response
        .receipts
        .iter()
        .find(|&r| matches!(r, Receipt::Call { .. }));

    assert!(call_response.is_some());

    assert_eq!(call_response.unwrap().amount().unwrap(), 0);
    assert_eq!(
        call_response.unwrap().asset_id().unwrap(),
        &AssetId::from(*contract_id.hash())
    );

    Ok(())
}

#[tokio::test]
async fn test_gas_errors() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let number_of_coins = 1;
    let amount_per_coin = 1_000_000;
    let coins = setup_single_asset_coins(
        wallet.address(),
        AssetId::zeroed(),
        number_of_coins,
        amount_per_coin,
    );

    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider);

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Test running out of gas. Gas price as `None` will be 0.
    let gas_limit = 100;
    let contract_instance_call = contract_instance
        .methods()
        .initialize_counter(42) // Build the ABI call
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit));

    //  Test that the call will use more gas than the gas limit
    let gas_used = contract_instance_call
        .estimate_transaction_cost(None, None)
        .await?
        .gas_used;
    assert!(gas_used > gas_limit);

    let response = contract_instance_call
        .call()
        .await
        .expect_err("should error");

    let expected = "transaction reverted: OutOfGas";
    assert!(response.to_string().starts_with(expected));

    // Test for insufficient base asset amount to pay for the transaction fee
    let response = contract_instance
        .methods()
        .initialize_counter(42) // Build the ABI call
        .with_tx_policies(TxPolicies::default().with_tip(100_000_000_000))
        .call()
        .await
        .expect_err("should error");

    let expected = "Response errors; Validity(InsufficientFeeAmount";
    assert!(response.to_string().contains(expected));

    Ok(())
}

#[tokio::test]
async fn test_call_param_gas_errors() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    // Transaction gas_limit is sufficient, call gas_forwarded is too small
    let contract_methods = contract_instance.methods();
    let response = contract_methods
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(446000))
        .call_params(CallParameters::default().with_gas_forwarded(1))?
        .call()
        .await
        .expect_err("should error");

    let expected = "transaction reverted: OutOfGas";
    assert!(response.to_string().starts_with(expected));

    // Call params gas_forwarded exceeds transaction limit
    let response = contract_methods
        .initialize_counter(42)
        .with_tx_policies(TxPolicies::default().with_script_gas_limit(1))
        .call_params(CallParameters::default().with_gas_forwarded(1_000))?
        .call()
        .await
        .expect_err("should error");

    assert!(response.to_string().contains(expected));
    Ok(())
}

#[tokio::test]
async fn test_get_gas_used() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let gas_used = contract_instance
        .methods()
        .initialize_counter(42)
        .call()
        .await?
        .gas_used;

    assert!(gas_used > 0);
    Ok(())
}

#[tokio::test]
async fn test_parse_block_time() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);
    let asset_id = AssetId::zeroed();
    let coins = setup_single_asset_coins(wallet.address(), asset_id, 1, DEFAULT_COIN_AMOUNT);
    let provider = setup_test_provider(coins.clone(), vec![], None, None).await?;
    wallet.set_provider(provider);
    let tx_policies = TxPolicies::default().with_script_gas_limit(2000);

    let wallet_2 = WalletUnlocked::new_random(None).lock();
    let (tx_id, _) = wallet
        .transfer(wallet_2.address(), 100, asset_id, tx_policies)
        .await?;

    let tx_response = wallet
        .try_provider()?
        .get_transaction_by_id(&tx_id)
        .await?
        .unwrap();
    assert!(tx_response.time.is_some());

    let block = wallet
        .try_provider()?
        .block_by_height(tx_response.block_height.unwrap())
        .await?
        .unwrap();
    assert!(block.header.time.is_some());

    Ok(())
}

#[tokio::test]
async fn test_get_spendable_with_exclusion() -> Result<()> {
    let coin_amount_1 = 1000;
    let coin_amount_2 = 500;

    let mut wallet = WalletUnlocked::new_random(None);
    let address = wallet.address();

    let coins = [coin_amount_1, coin_amount_2]
        .into_iter()
        .flat_map(|amount| setup_single_asset_coins(address, AssetId::zeroed(), 1, amount))
        .collect::<Vec<_>>();

    let message_amount = 200;
    let message = given_a_message(address.clone(), message_amount);

    let coin_1_utxo_id = coins[0].utxo_id;
    let coin_2_utxo_id = coins[1].utxo_id;

    let message_nonce = message.nonce;

    let provider = setup_test_provider(coins, vec![message], None, None).await?;

    wallet.set_provider(provider.clone());

    let requested_amount = coin_amount_1 + coin_amount_2 + message_amount;
    let consensus_parameters = provider.consensus_parameters().await?;
    {
        let resources = wallet
            .get_spendable_resources(
                *consensus_parameters.base_asset_id(),
                requested_amount,
                None,
            )
            .await
            .unwrap();
        assert_eq!(resources.len(), 3);
    }

    {
        let filter = ResourceFilter {
            from: wallet.address().clone(),
            amount: coin_amount_1,
            excluded_utxos: vec![coin_2_utxo_id],
            excluded_message_nonces: vec![message_nonce],
            ..Default::default()
        };
        let resources = provider.get_spendable_resources(filter).await.unwrap();

        match resources.as_slice() {
            [CoinType::Coin(coin)] => {
                assert_eq!(coin.utxo_id, coin_1_utxo_id);
            }
            _ => {
                panic!("This shouldn't happen!")
            }
        }
    }

    Ok(())
}

fn given_a_message(address: Bech32Address, message_amount: u64) -> Message {
    setup_single_message(
        &Bech32Address::default(),
        &address,
        message_amount,
        0.into(),
        vec![],
    )
}

fn convert_to_datetime(timestamp: u64) -> DateTime<Utc> {
    let unix = tai64::Tai64(timestamp).to_unix();
    DateTime::from_timestamp(unix, 0).unwrap()
}

/// This test is here in addition to `can_set_custom_block_time` because even though this test
/// passed, the Sway `timestamp` function didn't take into account the block time change. This
/// was fixed and this test is here to demonstrate the fix.
#[tokio::test]
async fn test_sway_timestamp() -> Result<()> {
    let block_time = 1u32; // seconds
    let provider_config = NodeConfig {
        block_production: Trigger::Interval {
            block_time: std::time::Duration::from_secs(block_time.into()),
        },
        ..NodeConfig::default()
    };
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(Some(1), Some(1), Some(100)),
        Some(provider_config),
        None,
    )
    .await?;
    let wallet = wallets.pop().unwrap();
    let provider = wallet.try_provider()?;

    setup_program_test!(
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/block_timestamp"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let origin_timestamp = provider.latest_block_time().await?.unwrap();
    let methods = contract_instance.methods();

    let response = methods.return_timestamp().call().await?;
    let mut expected_datetime =
        origin_timestamp.add(Duration::try_seconds(block_time as i64).unwrap());
    assert_eq!(convert_to_datetime(response.value), expected_datetime);

    let blocks_to_produce = 600;
    provider.produce_blocks(blocks_to_produce, None).await?;

    let response = methods.return_timestamp().call().await?;

    // `produce_blocks` call
    expected_datetime = expected_datetime
        .add(Duration::try_seconds((block_time * blocks_to_produce) as i64).unwrap());
    // method call
    expected_datetime = expected_datetime.add(Duration::try_seconds(block_time as i64).unwrap());

    assert_eq!(convert_to_datetime(response.value), expected_datetime);
    assert_eq!(
        provider.latest_block_time().await?.unwrap(),
        expected_datetime
    );
    Ok(())
}

#[cfg(feature = "coin-cache")]
async fn create_transfer(
    wallet: &WalletUnlocked,
    amount: u64,
    to: &Bech32Address,
) -> Result<ScriptTransaction> {
    let asset_id = AssetId::zeroed();
    let inputs = wallet
        .get_asset_inputs_for_amount(asset_id, amount, None)
        .await?;
    let outputs = wallet.get_asset_outputs_for_amount(to, asset_id, amount);

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
    tb.add_signer(wallet.clone())?;

    wallet.adjust_for_fee(&mut tb, amount).await?;

    tb.build(wallet.try_provider()?).await
}

#[cfg(feature = "coin-cache")]
#[tokio::test]
async fn transactions_with_the_same_utxo() -> Result<()> {
    use fuels::types::errors::transaction;

    let wallet_1 = launch_provider_and_get_wallet().await?;
    let provider = wallet_1.provider().unwrap();
    let wallet_2 = WalletUnlocked::new_random(Some(provider.clone()));

    let tx_1 = create_transfer(&wallet_1, 100, wallet_2.address()).await?;
    let tx_2 = create_transfer(&wallet_1, 101, wallet_2.address()).await?;

    let _tx_id = provider.send_transaction(tx_1).await?;
    let res = provider.send_transaction(tx_2).await;

    let err = res.expect_err("is error");

    assert!(matches!(
        err,
        Error::Transaction(transaction::Reason::Validation(..))
    ));
    assert!(err
        .to_string()
        .contains("was submitted recently in a transaction "));

    Ok(())
}

#[cfg(feature = "coin-cache")]
#[tokio::test]
async fn test_caching() -> Result<()> {
    let amount = 1000;
    let num_coins = 10;
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(Some(1), Some(num_coins), Some(amount)),
        Some(NodeConfig::default()),
        None,
    )
    .await?;
    let wallet_1 = wallets.pop().unwrap();
    let provider = wallet_1.provider().unwrap();
    let wallet_2 = WalletUnlocked::new_random(Some(provider.clone()));

    // Consecutively send transfer txs. Without caching, the txs will
    // end up trying to use the same input coins because 'get_spendable_coins()'
    // won't filter out recently used coins.
    let mut tx_ids = vec![];
    for _ in 0..10 {
        let tx = create_transfer(&wallet_1, 100, wallet_2.address()).await?;
        let tx_id = provider.send_transaction(tx).await?;
        tx_ids.push(tx_id);
    }

    provider.produce_blocks(10, None).await?;

    // Confirm all txs are settled
    for tx_id in tx_ids {
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));
    }

    // Verify the transfers were successful
    assert_eq!(wallet_2.get_asset_balance(&AssetId::zeroed()).await?, 1000);

    Ok(())
}

#[cfg(feature = "coin-cache")]
async fn create_revert_tx(wallet: &WalletUnlocked) -> Result<ScriptTransaction> {
    let script = std::fs::read("sway/scripts/reverting/out/release/reverting.bin")?;

    let amount = 1;
    let asset_id = AssetId::zeroed();
    let inputs = wallet
        .get_asset_inputs_for_amount(asset_id, amount, None)
        .await?;
    let outputs = wallet.get_asset_outputs_for_amount(&Bech32Address::default(), asset_id, amount);

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default())
        .with_script(script);
    tb.add_signer(wallet.clone())?;

    wallet.adjust_for_fee(&mut tb, amount).await?;

    tb.build(wallet.try_provider()?).await
}

#[cfg(feature = "coin-cache")]
#[tokio::test]
async fn test_cache_invalidation_on_await() -> Result<()> {
    let block_time = 1u32;
    let provider_config = NodeConfig {
        block_production: Trigger::Interval {
            block_time: std::time::Duration::from_secs(block_time.into()),
        },
        ..NodeConfig::default()
    };

    // create wallet with 1 coin so that the cache prevents further
    // spending unless the coin is invalidated from the cache
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(Some(1), Some(1), Some(100)),
        Some(provider_config),
        None,
    )
    .await?;
    let wallet = wallets.pop().unwrap();

    let provider = wallet.provider().unwrap();
    let tx = create_revert_tx(&wallet).await?;

    // Pause time so that the cache doesn't invalidate items based on TTL
    tokio::time::pause();

    // tx inputs should be cached and then invalidated due to the tx failing
    let tx_status = provider.send_transaction_and_await_commit(tx).await?;

    assert!(matches!(tx_status, TxStatus::Revert { .. }));

    let consensus_parameters = provider.consensus_parameters().await?;
    let coins = wallet
        .get_spendable_resources(*consensus_parameters.base_asset_id(), 1, None)
        .await?;
    assert_eq!(coins.len(), 1);

    Ok(())
}

#[tokio::test]
async fn can_fetch_mint_transactions() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let provider = wallet.try_provider()?;

    let transactions = provider
        .get_transactions(PaginationRequest {
            cursor: None,
            results: 100,
            direction: PageDirection::Forward,
        })
        .await?
        .results;

    // TODO: remove once (fuels-rs#1093)[https://github.com/FuelLabs/fuels-rs/issues/1093] is in
    // until then the type is explicitly mentioned to check that we're reexporting it through fuels
    let _: ::fuels::types::transaction::MintTransaction = transactions
        .into_iter()
        .find_map(|tx| match tx.transaction {
            TransactionType::Mint(tx) => Some(tx),
            _ => None,
        })
        .expect("Should have had at least one mint transaction");

    Ok(())
}

#[tokio::test]
async fn test_build_with_provider() -> Result<()> {
    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?;

    let receiver = WalletUnlocked::new_random(Some(provider.clone()));

    let consensus_parameters = provider.consensus_parameters().await?;
    let inputs = wallet
        .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), 100, None)
        .await?;
    let outputs = wallet.get_asset_outputs_for_amount(
        receiver.address(),
        *consensus_parameters.base_asset_id(),
        100,
    );

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
    tb.add_signer(wallet.clone())?;

    let tx = tb.build(provider).await?;

    provider.send_transaction_and_await_commit(tx).await?;

    let receiver_balance = receiver
        .get_asset_balance(consensus_parameters.base_asset_id())
        .await?;

    assert_eq!(receiver_balance, 100);

    Ok(())
}

#[tokio::test]
async fn can_produce_blocks_with_trig_never() -> Result<()> {
    let config = NodeConfig {
        block_production: Trigger::Never,
        ..NodeConfig::default()
    };
    let wallets =
        launch_custom_provider_and_get_wallets(WalletsConfig::default(), Some(config), None)
            .await?;
    let wallet = &wallets[0];
    let provider = wallet.try_provider()?;

    let consensus_parameters = provider.consensus_parameters().await?;
    let inputs = wallet
        .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), 100, None)
        .await?;
    let outputs = wallet.get_asset_outputs_for_amount(
        &Bech32Address::default(),
        *consensus_parameters.base_asset_id(),
        100,
    );

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
    tb.add_signer(wallet.clone())?;
    let tx = tb.build(provider).await?;
    let tx_id = tx.id(consensus_parameters.chain_id());

    provider.send_transaction(tx).await?;
    provider.produce_blocks(1, None).await?;

    tokio::time::sleep(std::time::Duration::from_millis(500)).await;
    let status = provider.tx_status(&tx_id).await?;
    assert!(matches!(status, TxStatus::Success { .. }));

    Ok(())
}

#[tokio::test]
async fn can_upload_executor_and_trigger_upgrade() -> Result<()> {
    let mut wallet = WalletUnlocked::new_random(None);

    // Need more coins to avoid "not enough coins to fit the target"
    let num_coins = 100;
    let coins = setup_single_asset_coins(
        wallet.address(),
        AssetId::zeroed(),
        num_coins,
        DEFAULT_COIN_AMOUNT,
    );

    let mut chain_config = ChainConfig::local_testnet();
    chain_config
        .consensus_parameters
        .set_privileged_address(wallet.address().into());

    let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?;
    wallet.set_provider(provider.clone());

    // This is downloaded over in `build.rs`
    let executor = std::fs::read(Path::new(env!("OUT_DIR")).join("fuel-core-wasm-executor.wasm"))?;

    let subsection_size = 65536;
    let subsections = UploadSubsection::split_bytecode(&executor, subsection_size).unwrap();

    let root = subsections[0].root;
    for subsection in subsections {
        let mut builder =
            UploadTransactionBuilder::prepare_subsection_upload(subsection, TxPolicies::default());
        wallet.add_witnesses(&mut builder)?;
        wallet.adjust_for_fee(&mut builder, 0).await?;
        let tx = builder.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;
    }

    let mut builder =
        UpgradeTransactionBuilder::prepare_state_transition_upgrade(root, TxPolicies::default());
    wallet.add_witnesses(&mut builder)?;
    wallet.adjust_for_fee(&mut builder, 0).await?;
    let tx = builder.build(provider.clone()).await?;

    provider.send_transaction(tx).await?;

    Ok(())
}

#[tokio::test]
async fn tx_respects_policies() -> Result<()> {
    setup_program_test!(
        Wallets("wallet"),
        Abigen(Contract(
            name = "TestContract",
            project = "e2e/sway/contracts/contract_test"
        )),
        Deploy(
            name = "contract_instance",
            contract = "TestContract",
            wallet = "wallet",
            random_salt = false,
        ),
    );

    let tip = 22;
    let witness_limit = 1000;
    let maturity = 4;
    let max_fee = 10_000;
    let script_gas_limit = 3000;
    let tx_policies = TxPolicies::new(
        Some(tip),
        Some(witness_limit),
        Some(maturity),
        Some(max_fee),
        Some(script_gas_limit),
    );

    // advance the block height to ensure the maturity is respected
    let provider = wallet.try_provider()?;
    provider.produce_blocks(4, None).await?;

    // trigger a transaction that contains script code to verify
    // that policies precede estimated values
    let response = contract_instance
        .methods()
        .initialize_counter(42)
        .with_tx_policies(tx_policies)
        .call()
        .await?;

    let tx_response = provider
        .get_transaction_by_id(&response.tx_id.unwrap())
        .await?
        .expect("tx should exist");
    let script = match tx_response.transaction {
        TransactionType::Script(tx) => tx,
        _ => panic!("expected script transaction"),
    };

    assert_eq!(script.maturity(), maturity as u32);
    assert_eq!(script.tip().unwrap(), tip);
    assert_eq!(script.witness_limit().unwrap(), witness_limit);
    assert_eq!(script.max_fee().unwrap(), max_fee);
    assert_eq!(script.gas_limit(), script_gas_limit);

    Ok(())
}

#[tokio::test]
#[ignore] //TODO: https://github.com/FuelLabs/fuels-rs/issues/1581
async fn can_setup_static_gas_price() -> Result<()> {
    let expected_gas_price = 474;
    let node_config = NodeConfig {
        starting_gas_price: expected_gas_price,
        ..Default::default()
    };
    let provider = setup_test_provider(vec![], vec![], Some(node_config), None).await?;

    let gas_price = provider.estimate_gas_price(0).await?.gas_price;

    let da_cost = 1000;
    assert_eq!(gas_price, da_cost + expected_gas_price);

    Ok(())
}

#[tokio::test]
async fn tx_with_witness_data() -> Result<()> {
    use fuel_asm::{op, GTFArgs};

    let wallet = launch_provider_and_get_wallet().await?;
    let provider = wallet.try_provider()?;

    let receiver = WalletUnlocked::new_random(Some(provider.clone()));

    let consensus_parameters = provider.consensus_parameters().await?;
    let inputs = wallet
        .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), 10000, None)
        .await?;
    let outputs = wallet.get_asset_outputs_for_amount(
        receiver.address(),
        *consensus_parameters.base_asset_id(),
        1,
    );

    let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
    tb.add_signer(wallet.clone())?;

    // we test that the witness data wasn't tempered with during the build (gas estimation) process
    // if the witness data is tempered with, the estimation will be off and the transaction
    // will error out with `OutOfGas`
    let script: Vec<u8> = vec![
        // load witness data into register 0x10
        op::gtf(0x10, 0x00, GTFArgs::WitnessData.into()),
        op::lw(0x10, 0x10, 0x00),
        // load expected value into register 0x11
        op::movi(0x11, 0x0f),
        // load the offset of the revert instruction into register 0x12
        op::movi(0x12, 0x08),
        // compare the two values and jump to the revert instruction if they are not equal
        op::jne(0x10, 0x11, 0x12),
        // do some expensive operation so gas estimation is higher if comparison passes
        op::gtf(0x13, 0x01, GTFArgs::WitnessData.into()),
        op::gtf(0x14, 0x01, GTFArgs::WitnessDataLength.into()),
        op::aloc(0x14),
        op::eck1(RegId::HP, 0x13, 0x13),
        // return the witness data
        op::ret(0x10),
        op::rvrt(RegId::ZERO),
    ]
    .into_iter()
    .collect();
    tb.script = script;

    let expected_data = 15u64;
    let witness = Witness::from(expected_data.to_be_bytes().to_vec());
    tb.witnesses_mut().push(witness);

    let tx = tb
        .with_tx_policies(TxPolicies::default().with_witness_limit(1000))
        .build(provider)
        .await?;

    let status = provider.send_transaction_and_await_commit(tx).await?;

    match status {
        TxStatus::Success { receipts } => {
            let ret: u64 = receipts
                .into_iter()
                .find_map(|receipt| match receipt {
                    Receipt::Return { val, .. } => Some(val),
                    _ => None,
                })
                .expect("should have return value");

            assert_eq!(ret, expected_data);
        }
        _ => panic!("expected success status"),
    }

    Ok(())
}

#[tokio::test]
async fn contract_call_with_impersonation() -> Result<()> {
    let provider_config = NodeConfig {
        utxo_validation: false,
        ..NodeConfig::default()
    };
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(Some(1), Some(10), Some(1000)),
        Some(provider_config),
        None,
    )
    .await?;
    let wallet = wallets.pop().unwrap();
    let provider = wallet.try_provider()?;

    let impersonator = ImpersonatedAccount::new(wallet.address().clone(), Some(provider.clone()));

    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    let contract_id = Contract::load_from(
        "sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy_if_not_exists(&wallet, TxPolicies::default())
    .await?;

    let contract_instance = MyContract::new(contract_id, impersonator.clone());

    // The gas used by the script to call a contract and forward remaining gas limit.
    contract_instance
        .methods()
        .initialize_counter(42)
        .call()
        .await?;

    Ok(())
}

#[tokio::test]
async fn is_account_query_test() -> Result<()> {
    {
        let wallet = launch_provider_and_get_wallet().await?;
        let provider = wallet.provider().unwrap().clone();

        let blob = Blob::new(vec![1; 100]);
        let blob_id = blob.id();

        let is_account = provider.is_user_account(blob_id).await?;
        assert!(is_account);

        let mut tb = BlobTransactionBuilder::default().with_blob(blob);
        wallet.adjust_for_fee(&mut tb, 0).await?;
        wallet.add_witnesses(&mut tb)?;
        let tx = tb.build(provider.clone()).await?;

        provider
            .send_transaction_and_await_commit(tx)
            .await?
            .check(None)?;

        let is_account = provider.is_user_account(blob_id).await?;
        assert!(!is_account);
    }
    {
        let wallet = launch_provider_and_get_wallet().await?;
        let provider = wallet.provider().unwrap().clone();

        let contract = Contract::load_from(
            "sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?;
        let contract_id = contract.contract_id();

        let is_account = provider.is_user_account(*contract_id).await?;
        assert!(is_account);

        contract.deploy(&wallet, TxPolicies::default()).await?;

        let is_account = provider.is_user_account(*contract_id).await?;
        assert!(!is_account);
    }
    {
        let wallet = launch_provider_and_get_wallet().await?;
        let provider = wallet.provider().unwrap().clone();

        let mut tb = ScriptTransactionBuilder::default();
        wallet.adjust_for_fee(&mut tb, 0).await?;
        wallet.add_witnesses(&mut tb)?;
        let tx = tb.build(provider.clone()).await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let tx_id = tx.id(consensus_parameters.chain_id());
        let is_account = provider.is_user_account(tx_id).await?;
        assert!(is_account);

        provider
            .send_transaction_and_await_commit(tx)
            .await?
            .check(None)?;
        let is_account = provider.is_user_account(tx_id).await?;
        assert!(!is_account);
    }

    Ok(())
}\n```

Cookbook

This section covers more advanced use cases that can be satisfied by combining various features of the Rust SDK. As such, it assumes that you have already made yourself familiar with the previous chapters of this book.

Note This section is still a work in progress and more recipes may be added in the future.

Custom chain

This example demonstrates how to start a short-lived Fuel node with custom consensus parameters for the underlying chain.

First, we have to import ConsensusParameters and ChainConfig:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Next, we can define some values for the consensus parameters:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Before we can start a node, we probably also want to define some genesis coins and assign them to an address:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Finally, we call setup_test_provider(), which starts a node with the given configurations and returns a provider attached to that node:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Deposit and withdraw

Consider the following contract:

```sway\ncontract;

use std::{
    asset::{
        mint_to,
        transfer,
    },
    call_frames::{
        msg_asset_id,
    },
    constants::ZERO_B256,
    context::msg_amount,
};

abi LiquidityPool {
    #[payable]
    fn deposit(recipient: Identity);
    #[payable]
    fn withdraw(recipient: Identity);
}

const BASE_TOKEN: AssetId = AssetId::from(0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c);

impl LiquidityPool for Contract {
    #[payable]
    fn deposit(recipient: Identity) {
        assert(BASE_TOKEN == msg_asset_id());
        assert(0 < msg_amount());

        // Mint two times the amount.
        let amount_to_mint = msg_amount() * 2;

        // Mint some LP token based upon the amount of the base token.
        mint_to(recipient, ZERO_B256, amount_to_mint);
    }

    #[payable]
    fn withdraw(recipient: Identity) {
        assert(0 < msg_amount());

        // Amount to withdraw.
        let amount_to_transfer = msg_amount() / 2;

        // Transfer base token to recipient.
        transfer(recipient, BASE_TOKEN, amount_to_transfer);
    }
}\n```

As its name suggests, it represents a simplified example of a liquidity pool contract. The method deposit() expects you to supply an arbitrary amount of the BASE_TOKEN. As a result, it mints double the amount of the liquidity asset to the calling address. Analogously, if you call withdraw() supplying it with the liquidity asset, it will transfer half that amount of the BASE_TOKEN back to the calling address except for deducting it from the contract balance instead of minting it.

The first step towards interacting with any contract in the Rust SDK is calling the abigen! macro to generate type-safe Rust bindings for the contract methods:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Next, we set up a wallet with custom-defined assets. We give our wallet some of the contracts BASE_TOKEN and the default asset (required for contract deployment):

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Having launched a provider and created the wallet, we can deploy our contract and create an instance of its methods:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

With the preparations out of the way, we can finally deposit to the liquidity pool by calling deposit() via the contract instance. Since we have to transfer assets to the contract, we create the appropriate CallParameters and chain them to the method call. To receive the minted liquidity pool asset, we have to append a variable output to our contract call.

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

As a final demonstration, let's use all our liquidity asset balance to withdraw from the pool and confirm we retrieved the initial amount. For this, we get our liquidity asset balance and supply it to the withdraw() call via CallParameters.

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Transfer all assets

The transfer() method lets you transfer a single asset, but what if you needed to move all of your assets to a different wallet? You could repeatably call transfer(), initiating a transaction each time, or you bundle all the transfers into a single transaction. This chapter guides you through crafting your custom transaction for transferring all assets owned by a wallet.

Lets quickly go over the setup:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

We prepare two wallets with randomized addresses. Next, we want one of our wallets to have some random assets, so we set them up with setup_multiple_assets_coins(). Having created the coins, we can start a provider and assign it to the previously created wallets.

Transactions require us to define input and output coins. Let's assume we do not know the assets owned by wallet_1. We retrieve its balances, i.e. tuples consisting of a string representing the asset ID and the respective amount. This lets us use the helpers get_asset_inputs_for_amount(), get_asset_outputs_for_amount() to create the appropriate inputs and outputs.

We transfer only a part of the base asset balance so that the rest can cover transaction fees:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

All that is left is to build the transaction via ScriptTransactionBuilder, have wallet_1 sign it, and we can send it. We confirm this by checking the number of balances present in the receiving wallet and their amount:

```rust\n#[cfg(test)]
mod tests {
    use std::{str::FromStr, time::Duration};

    use fuels::{
        accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
        prelude::Result,
        test_helpers::{setup_single_asset_coins, setup_test_provider},
        types::{
            bech32::Bech32Address,
            transaction::TxPolicies,
            transaction_builders::{
                BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder,
            },
            tx_status::TxStatus,
            AssetId,
        },
    };

    #[tokio::test]
    async fn liquidity() -> Result<()> {
        use fuels::{
            prelude::*,
            test_helpers::{AssetConfig, WalletsConfig},
            types::Bits256,
        };

        // ANCHOR: liquidity_abigen
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
        ));
        // ANCHOR_END: liquidity_abigen

        // ANCHOR: liquidity_wallet
        let base_asset_id: AssetId =
            "0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;

        let asset_ids = [AssetId::zeroed(), base_asset_id];
        let asset_configs = asset_ids
            .map(|id| AssetConfig {
                id,
                num_coins: 1,
                coin_amount: 1_000_000,
            })
            .into();

        let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
        let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
        let wallet = &wallets[0];
        // ANCHOR_END: liquidity_wallet

        // ANCHOR: liquidity_deploy
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
            LoadConfiguration::default(),
        )?
        .deploy(wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR_END: liquidity_deploy

        // ANCHOR: liquidity_deposit
        let deposit_amount = 1_000_000;
        let call_params = CallParameters::default()
            .with_amount(deposit_amount)
            .with_asset_id(base_asset_id);

        contract_methods
            .deposit(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: liquidity_deposit

        // ANCHOR: liquidity_withdraw
        let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
        let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;

        let call_params = CallParameters::default()
            .with_amount(lp_token_balance)
            .with_asset_id(lp_asset_id);

        contract_methods
            .withdraw(wallet.address().into())
            .call_params(call_params)?
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;

        let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
        assert_eq!(base_balance, deposit_amount);
        // ANCHOR_END: liquidity_withdraw
        Ok(())
    }

    #[tokio::test]
    async fn custom_chain() -> Result<()> {
        // ANCHOR: custom_chain_import
        use fuels::{
            prelude::*,
            tx::{ConsensusParameters, FeeParameters, TxParameters},
        };
        // ANCHOR_END: custom_chain_import

        // ANCHOR: custom_chain_consensus
        let tx_params = TxParameters::default()
            .with_max_gas_per_tx(1_000)
            .with_max_inputs(2);
        let fee_params = FeeParameters::default().with_gas_price_factor(10);

        let mut consensus_parameters = ConsensusParameters::default();
        consensus_parameters.set_tx_params(tx_params);
        consensus_parameters.set_fee_params(fee_params);

        let chain_config = ChainConfig {
            consensus_parameters,
            ..ChainConfig::default()
        };
        // ANCHOR_END: custom_chain_consensus

        // ANCHOR: custom_chain_coins
        let wallet = WalletUnlocked::new_random(None);
        let coins = setup_single_asset_coins(
            wallet.address(),
            Default::default(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        // ANCHOR_END: custom_chain_coins

        // ANCHOR: custom_chain_provider
        let node_config = NodeConfig::default();
        let _provider =
            setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?;
        // ANCHOR_END: custom_chain_provider
        Ok(())
    }

    #[tokio::test]
    async fn transfer_multiple() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;
        // ANCHOR: transfer_multiple_setup
        let mut wallet_1 = WalletUnlocked::new_random(None);
        let mut wallet_2 = WalletUnlocked::new_random(None);

        const NUM_ASSETS: u64 = 5;
        const AMOUNT: u64 = 100_000;
        const NUM_COINS: u64 = 1;
        let (coins, _) =
            setup_multiple_assets_coins(wallet_1.address(), NUM_ASSETS, NUM_COINS, AMOUNT);

        let provider = setup_test_provider(coins, vec![], None, None).await?;

        wallet_1.set_provider(provider.clone());
        wallet_2.set_provider(provider.clone());
        // ANCHOR_END: transfer_multiple_setup

        // ANCHOR: transfer_multiple_input
        let balances = wallet_1.get_balances().await?;

        let consensus_parameters = provider.consensus_parameters().await?;
        let mut inputs = vec![];
        let mut outputs = vec![];
        for (id_string, amount) in balances {
            let id = AssetId::from_str(&id_string)?;
            let amount = amount as u64;

            let input = wallet_1
                .get_asset_inputs_for_amount(id, amount, None)
                .await?;
            inputs.extend(input);

            // we don't transfer the full base asset so we can cover fees
            let output = if id == *consensus_parameters.base_asset_id() {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2)
            } else {
                wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount)
            };

            outputs.extend(output);
        }
        // ANCHOR_END: transfer_multiple_input

        // ANCHOR: transfer_multiple_transaction
        let mut tb =
            ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
        tb.add_signer(wallet_1.clone())?;

        let tx = tb.build(&provider).await?;

        provider.send_transaction_and_await_commit(tx).await?;

        let balances = wallet_2.get_balances().await?;

        assert_eq!(balances.len(), NUM_ASSETS as usize);
        for (id, balance) in balances {
            if id == *consensus_parameters.base_asset_id().to_string() {
                assert_eq!(balance, (AMOUNT / 2) as u128);
            } else {
                assert_eq!(balance, AMOUNT as u128);
            }
        }
        // ANCHOR_END: transfer_multiple_transaction

        Ok(())
    }

    #[tokio::test]
    #[cfg(any(not(feature = "fuel-core-lib"), feature = "rocksdb"))]
    async fn create_or_use_rocksdb() -> Result<()> {
        use std::path::PathBuf;

        use fuels::prelude::*;
        // ANCHOR: create_or_use_rocksdb
        let provider_config = NodeConfig {
            database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))),
            ..NodeConfig::default()
        };
        // ANCHOR_END: create_or_use_rocksdb

        launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None)
            .await?;

        Ok(())
    }

    #[tokio::test]
    async fn custom_transaction() -> Result<()> {
        let mut hot_wallet = WalletUnlocked::new_random(None);
        let mut cold_wallet = WalletUnlocked::new_random(None);

        let code_path = "../../e2e/sway/predicates/swap/out/release/swap.bin";
        let mut predicate = Predicate::load_from(code_path)?;

        let num_coins = 5;
        let amount = 1000;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let base_coins =
            setup_single_asset_coins(hot_wallet.address(), AssetId::zeroed(), num_coins, amount);
        let other_coins =
            setup_single_asset_coins(predicate.address(), bridged_asset_id, num_coins, amount);

        let provider = setup_test_provider(
            base_coins.into_iter().chain(other_coins).collect(),
            vec![],
            None,
            None,
        )
        .await?;

        hot_wallet.set_provider(provider.clone());
        cold_wallet.set_provider(provider.clone());
        predicate.set_provider(provider.clone());

        // ANCHOR: custom_tx_receiver
        let ask_amount = 100;
        let locked_amount = 500;
        let bridged_asset_id = AssetId::from([1u8; 32]);
        let receiver = Bech32Address::from_str(
            "fuel1p8qt95dysmzrn2rmewntg6n6rg3l8ztueqafg5s6jmd9cgautrdslwdqdw",
        )?;
        // ANCHOR_END: custom_tx_receiver

        // ANCHOR: custom_tx
        let tb = ScriptTransactionBuilder::default();
        // ANCHOR_END: custom_tx

        // ANCHOR: custom_tx_io_base
        let consensus_parameters = provider.consensus_parameters().await?;
        let base_inputs = hot_wallet
            .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None)
            .await?;
        let base_outputs = hot_wallet.get_asset_outputs_for_amount(
            &receiver,
            *consensus_parameters.base_asset_id(),
            ask_amount,
        );
        // ANCHOR_END: custom_tx_io_base

        // ANCHOR: custom_tx_io_other
        let other_asset_inputs = predicate
            .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None)
            .await?;
        let other_asset_outputs =
            predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);
        // ANCHOR_END: custom_tx_io_other

        // ANCHOR: custom_tx_io
        let inputs = base_inputs
            .into_iter()
            .chain(other_asset_inputs.into_iter())
            .collect();
        let outputs = base_outputs
            .into_iter()
            .chain(other_asset_outputs.into_iter())
            .collect();

        let mut tb = tb.with_inputs(inputs).with_outputs(outputs);
        // ANCHOR_END: custom_tx_io

        // ANCHOR: custom_tx_add_signer
        tb.add_signer(hot_wallet.clone())?;
        // ANCHOR_END: custom_tx_add_signer

        // ANCHOR: custom_tx_adjust
        hot_wallet.adjust_for_fee(&mut tb, 100).await?;
        // ANCHOR_END: custom_tx_adjust

        // ANCHOR: custom_tx_policies
        let tx_policies = TxPolicies::default().with_tip(1);
        let tb = tb.with_tx_policies(tx_policies);
        // ANCHOR_END: custom_tx_policies

        // ANCHOR: custom_tx_build
        let tx = tb.build(&provider).await?;
        let tx_id = provider.send_transaction(tx).await?;
        // ANCHOR_END: custom_tx_build

        tokio::time::sleep(Duration::from_millis(500)).await;
        // ANCHOR: custom_tx_verify
        let status = provider.tx_status(&tx_id).await?;
        assert!(matches!(status, TxStatus::Success { .. }));

        let balance = cold_wallet.get_asset_balance(&bridged_asset_id).await?;
        assert_eq!(balance, locked_amount);
        // ANCHOR_END: custom_tx_verify

        Ok(())
    }
}\n```

Debugging

note This section is still a work in progress.

Function selector

Whenever you call a contract method the SDK will generate a function selector according to the fuel specs which will be used by the node to identify which method we wish to execute.

If, for whatever reason, you wish to generate the function selector yourself you can do so:

```rust\n#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use fuel_abi_types::abi::unified_program::UnifiedProgramABI;
    use fuels::{
        core::codec::ABIDecoder,
        macros::abigen,
        types::{errors::Result, param_types::ParamType, SizedAsciiString},
    };

    #[test]
    fn encode_fn_selector() {
        use fuels::core::codec::encode_fn_selector;

        // ANCHOR: example_fn_selector
        // fn some_fn_name(arg1: Vec<str[3]>, arg2: u8)
        let fn_name = "some_fn_name";

        let selector = encode_fn_selector(fn_name);

        assert_eq!(
            selector,
            [0, 0, 0, 0, 0, 0, 0, 12, 115, 111, 109, 101, 95, 102, 110, 95, 110, 97, 109, 101]
        );
        // ANCHOR_END: example_fn_selector
    }

    #[test]
    fn decoded_debug_matches_rust_debug() -> Result<()> {
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/types/contracts/generics/out/release/generics-abi.json"
        ));

        let json_abi_file = "../../e2e/sway/types/contracts/generics/out/release/generics-abi.json";
        let abi_file_contents = std::fs::read_to_string(json_abi_file)?;

        let parsed_abi = UnifiedProgramABI::from_json_abi(&abi_file_contents)?;

        let type_lookup = parsed_abi
            .types
            .into_iter()
            .map(|decl| (decl.type_id, decl))
            .collect::<HashMap<_, _>>();

        let get_first_fn_argument = |fn_name: &str| {
            parsed_abi
                .functions
                .iter()
                .find(|abi_fun| abi_fun.name == fn_name)
                .expect("should be there")
                .inputs
                .first()
                .expect("should be there")
        };
        let decoder = ABIDecoder::default();

        {
            // simple struct with a single generic parameter
            let type_application = get_first_fn_argument("struct_w_generic");
            let param_type = ParamType::try_from_type_application(type_application, &type_lookup)?;

            let expected_struct = SimpleGeneric {
                single_generic_param: 123u64,
            };

            assert_eq!(
                format!("{expected_struct:?}"),
                decoder.decode_as_debug_str(&param_type, [0, 0, 0, 0, 0, 0, 0, 123].as_slice())?
            );
        }
        {
            // struct that delegates the generic param internally
            let type_application = get_first_fn_argument("struct_delegating_generic");
            let param_type = ParamType::try_from_type_application(type_application, &type_lookup)?;

            let expected_struct = PassTheGenericOn {
                one: SimpleGeneric {
                    single_generic_param: SizedAsciiString::<3>::try_from("abc")?,
                },
            };

            assert_eq!(
                format!("{expected_struct:?}"),
                decoder.decode_as_debug_str(&param_type, [97, 98, 99].as_slice())?
            );
        }
        {
            // enum with generic in variant
            let type_application = get_first_fn_argument("enum_w_generic");
            let param_type = ParamType::try_from_type_application(type_application, &type_lookup)?;

            let expected_enum = EnumWGeneric::B(10u64);

            assert_eq!(
                format!("{expected_enum:?}"),
                decoder.decode_as_debug_str(
                    &param_type,
                    [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 10].as_slice()
                )?
            );
        }
        {
            // logged type
            let logged_type = parsed_abi
                .logged_types
                .as_ref()
                .expect("has logs")
                .first()
                .expect("has log");

            let param_type =
                ParamType::try_from_type_application(&logged_type.application, &type_lookup)?;

            let expected_u8 = 1;

            assert_eq!(
                format!("{expected_u8}"),
                decoder.decode_as_debug_str(&param_type, [1].as_slice())?
            );
        }

        Ok(())
    }
}\n```

Decoding script transactions

The SDK offers some tools that can help you make fuel script transactions more human readable. You can determine whether the script transaction is:

  • calling a contract method(s),
  • is a loader script and you can see the blob id
  • is neither of the above

In the case of contract call(s), if you have the ABI file, you can also decode the arguments to the function by making use of the AbiFormatter:

```rust\n#[cfg(test)]
mod tests {
    use std::{collections::HashSet, time::Duration};

    use fuels::{
        core::codec::{encode_fn_selector, ABIFormatter, DecoderConfig, EncoderConfig},
        crypto::SecretKey,
        prelude::{LoadConfiguration, NodeConfig, StorageConfiguration},
        programs::debug::ScriptType,
        test_helpers::{ChainConfig, StateConfig},
        types::{
            errors::{transaction::Reason, Result},
            Bits256,
        },
    };
    use rand::Rng;

    #[tokio::test]
    async fn instantiate_client() -> Result<()> {
        // ANCHOR: instantiate_client
        use fuels::prelude::{FuelService, Provider};

        // Run the fuel node.
        let server = FuelService::start(
            NodeConfig::default(),
            ChainConfig::default(),
            StateConfig::default(),
        )
        .await?;

        // Create a client that will talk to the node created above.
        let client = Provider::from(server.bound_address()).await?;
        assert!(client.healthy().await?);
        // ANCHOR_END: instantiate_client
        Ok(())
    }

    #[tokio::test]
    async fn deploy_contract() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract
        // This helper will launch a local node and provide a test wallet linked to it
        let wallet = launch_provider_and_get_wallet().await?;

        // This will load and deploy your contract binary to the chain so that its ID can
        // be used to initialize the instance
        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR_END: deploy_contract

        Ok(())
    }

    #[tokio::test]
    async fn setup_program_test_example() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deploy_contract_setup_macro_short
        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: deploy_contract_setup_macro_short

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_cost_estimation
        let contract_instance = MyContract::new(contract_id, wallet);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: contract_call_cost_estimation

        let expected_gas = 2816;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_parameters() -> std::result::Result<(), Box<dyn std::error::Error>> {
        use fuels::{prelude::*, tx::StorageSlot, types::Bytes32};
        use rand::prelude::{Rng, SeedableRng, StdRng};

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");

        // ANCHOR: deploy_with_parameters
        // Optional: Add `Salt`
        let rng = &mut StdRng::seed_from_u64(2322u64);
        let salt: [u8; 32] = rng.gen();

        // Optional: Configure storage
        let key = Bytes32::from([1u8; 32]);
        let value = Bytes32::from([2u8; 32]);
        let storage_slot = StorageSlot::new(key, value);
        let storage_configuration =
            StorageConfiguration::default().add_slot_overrides([storage_slot]);
        let configuration = LoadConfiguration::default()
            .with_storage_configuration(storage_configuration)
            .with_salt(salt);

        // Optional: Configure deployment parameters
        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            configuration,
        )?
        .deploy(&wallet, tx_policies)
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        // ANCHOR_END: deploy_with_parameters

        assert_ne!(contract_id_1, contract_id_2);

        // ANCHOR: use_deployed_contract
        // This will generate your contract's methods onto `MyContract`.
        // This means an instance of `MyContract` will have access to all
        // your contract's methods that are running on-chain!
        // ANCHOR: abigen_example
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        // ANCHOR_END: abigen_example

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id_2, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);
        // ANCHOR_END: use_deployed_contract

        // ANCHOR: submit_response_contract
        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .submit()
            .await?;

        tokio::time::sleep(Duration::from_millis(500)).await;
        let value = response.response().await?.value;

        // ANCHOR_END: submit_response_contract
        assert_eq!(42, value);

        Ok(())
    }

    #[tokio::test]
    async fn deploy_with_multiple_wallets() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallets =
            launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;

        let contract_id_1 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallets[0], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_1}");
        let contract_instance_1 = MyContract::new(contract_id_1, wallets[0].clone());

        let response = contract_instance_1
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);

        let contract_id_2 = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default().with_salt([1; 32]),
        )?
        .deploy(&wallets[1], TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id_2}");
        let contract_instance_2 = MyContract::new(contract_id_2, wallets[1].clone());

        let response = contract_instance_2
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call()
            .await?;

        assert_eq!(42, response.value);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn contract_tx_and_call_params() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        // ANCHOR: tx_policies
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();

        let tx_policies = TxPolicies::default()
            .with_tip(1)
            .with_script_gas_limit(1_000_000)
            .with_maturity(0);

        let response = contract_methods
            .initialize_counter(42) // Our contract method
            .with_tx_policies(tx_policies) // Chain the tx policies
            .call() // Perform the contract call
            .await?; // This is an async call, `.await` it.
                     // ANCHOR_END: tx_policies

        // ANCHOR: tx_policies_default
        let response = contract_methods
            .initialize_counter(42)
            .with_tx_policies(TxPolicies::default())
            .call()
            .await?;
        // ANCHOR_END: tx_policies_default

        // ANCHOR: call_parameters
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let tx_policies = TxPolicies::default();

        // Forward 1_000_000 coin amount of base asset_id
        // this is a big number for checking that amount can be a u64
        let call_params = CallParameters::default().with_amount(1_000_000);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_parameters

        // ANCHOR: call_parameters_default
        let response = contract_methods
            .initialize_counter(42)
            .call_params(CallParameters::default())?
            .call()
            .await?;
        // ANCHOR_END: call_parameters_default
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn token_ops_tests() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/token_ops/out/release/token_ops\
        .bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        println!("Contract deployed @ {contract_id}");
        let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
        // ANCHOR: simulate
        // you would mint 100 coins if the transaction wasn't simulated
        let counter = contract_methods
            .mint_coins(100)
            .simulate(Execution::Realistic)
            .await?;
        // ANCHOR_END: simulate

        {
            let contract_id = contract_id.clone();
            // ANCHOR: simulate_read_state
            // you don't need any funds to read state
            let balance = contract_methods
                .get_balance(contract_id, AssetId::zeroed())
                .simulate(Execution::StateReadOnly)
                .await?
                .value;
            // ANCHOR_END: simulate_read_state
        }

        let response = contract_methods.mint_coins(1_000_000).call().await?;
        // ANCHOR: variable_outputs
        let address = wallet.address();
        let asset_id = contract_id.asset_id(&Bits256::zeroed());

        // withdraw some tokens to wallet
        let response = contract_methods
            .transfer(1_000_000, asset_id, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .call()
            .await?;
        // ANCHOR_END: variable_outputs
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn dependency_estimation() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let called_contract_id: ContractId = Contract::load_from(
            "../../e2e/sway/contracts/lib_contract/out/release/lib_contract.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?
        .into();

        let bin_path =
            "../../e2e/sway/contracts/lib_contract_caller/out/release/lib_contract_caller.bin";
        let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())?
            .deploy(&wallet, TxPolicies::default())
            .await?;

        let contract_methods =
            MyContract::new(caller_contract_id.clone(), wallet.clone()).methods();

        // ANCHOR: dependency_estimation_fail
        let address = wallet.address();
        let amount = 100;

        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .call()
            .await;

        assert!(matches!(
            response,
            Err(Error::Transaction(Reason::Reverted { .. }))
        ));
        // ANCHOR_END: dependency_estimation_fail

        // ANCHOR: dependency_estimation_manual
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
            .with_contract_ids(&[called_contract_id.into()])
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation_manual

        let asset_id = caller_contract_id.asset_id(&Bits256::zeroed());
        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, amount);

        // ANCHOR: dependency_estimation
        let response = contract_methods
            .mint_then_increment_from_contract(called_contract_id, amount, address.into())
            .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
            .determine_missing_contracts(Some(2))
            .await?
            .call()
            .await?;
        // ANCHOR_END: dependency_estimation

        let balance = wallet.get_asset_balance(&asset_id).await?;
        assert_eq!(balance, 2 * amount);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn get_contract_outputs() -> Result<()> {
        use fuels::prelude::*;

        // ANCHOR: deployed_contracts
        abigen!(Contract(
            name = "MyContract",
            // Replace with your contract ABI.json path
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));
        let wallet_original = launch_provider_and_get_wallet().await?;

        let wallet = wallet_original.clone();
        // Your bech32m encoded contract ID.
        let contract_id: Bech32ContractId =
            "fuel1vkm285ypjesypw7vhdlhnty3kjxxx4efckdycqh3ttna4xvmxtfs6murwy".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // You can now use the `connected_contract_instance` just as you did above!
        // ANCHOR_END: deployed_contracts

        let wallet = wallet_original;
        // ANCHOR: deployed_contracts_hex
        let contract_id: ContractId =
            "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

        let connected_contract_instance = MyContract::new(contract_id, wallet);
        // ANCHOR_END: deployed_contracts_hex

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn call_params_gas() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: call_params_gas
        // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
        // the contract call transaction may consume up to 1_000_000 gas, while the actual call may
        // only use 4300 gas
        let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
        let call_params = CallParameters::default().with_gas_forwarded(4300);

        let response = contract_methods
            .get_msg_amount() // Our contract method.
            .with_tx_policies(tx_policies) // Chain the tx policies.
            .call_params(call_params)? // Chain the call parameters.
            .call() // Perform the contract call.
            .await?;
        // ANCHOR_END: call_params_gas
        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_example() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: multi_call_prepare
        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);
        // ANCHOR_END: multi_call_prepare

        // ANCHOR: multi_call_build
        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);
        // ANCHOR_END: multi_call_build
        let multi_call_handler_tmp = multi_call_handler.clone();

        // ANCHOR: multi_call_values
        let (counter, array): (u64, [u64; 2]) = multi_call_handler.call().await?.value;
        // ANCHOR_END: multi_call_values

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: multi_contract_call_response
        let response = multi_call_handler.call::<(u64, [u64; 2])>().await?;
        // ANCHOR_END: multi_contract_call_response

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        let multi_call_handler = multi_call_handler_tmp.clone();
        // ANCHOR: submit_response_multicontract
        let submitted_tx = multi_call_handler.submit().await?;
        tokio::time::sleep(Duration::from_millis(500)).await;
        let (counter, array): (u64, [u64; 2]) = submitted_tx.response().await?.value;
        // ANCHOR_END: submit_response_multicontract

        assert_eq!(counter, 42);
        assert_eq!(array, [42; 2]);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn multi_call_cost_estimation() -> Result<()> {
        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let wallet = launch_provider_and_get_wallet().await?;

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

        // ANCHOR: multi_call_cost_estimation
        let call_handler_1 = contract_methods.initialize_counter(42);
        let call_handler_2 = contract_methods.get_array([42; 2]);

        let multi_call_handler = CallHandler::new_multi_call(wallet.clone())
            .add_call(call_handler_1)
            .add_call(call_handler_2);

        let tolerance = Some(0.0);
        let block_horizon = Some(1);
        let transaction_cost = multi_call_handler
            .estimate_transaction_cost(tolerance, block_horizon) // Get estimated transaction cost
            .await?;
        // ANCHOR_END: multi_call_cost_estimation

        let expected_gas = 4402;

        assert_eq!(transaction_cost.gas_used, expected_gas);

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn connect_wallet() -> Result<()> {
        use fuels::prelude::*;
        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let config = WalletsConfig::new(Some(2), Some(1), Some(DEFAULT_COIN_AMOUNT));
        let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
        let wallet_1 = wallets.pop().unwrap();
        let wallet_2 = wallets.pop().unwrap();

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet_1, TxPolicies::default())
        .await?;

        // ANCHOR: connect_wallet
        // Create contract instance with wallet_1
        let contract_instance = MyContract::new(contract_id, wallet_1.clone());

        // Perform contract call with wallet_2
        let response = contract_instance
            .with_account(wallet_2) // Connect wallet_2
            .methods() // Get contract methods
            .get_msg_amount() // Our contract method
            .call() // Perform the contract call.
            .await?; // This is an async call, `.await` for it.
                     // ANCHOR_END: connect_wallet

        Ok(())
    }

    #[tokio::test]
    async fn custom_assets_example() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let other_wallet = WalletUnlocked::new_random(None);

        // ANCHOR: add_custom_assets
        let amount = 1000;
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .add_custom_asset(
                AssetId::zeroed(),
                amount,
                Some(other_wallet.address().clone()),
            )
            .call()
            .await?;
        // ANCHOR_END: add_custom_assets

        Ok(())
    }

    #[tokio::test]
    async fn low_level_call_example() -> Result<()> {
        use fuels::{core::codec::calldata, prelude::*, types::SizedAsciiString};

        setup_program_test!(
            Wallets("wallet"),
            Abigen(
                Contract(
                    name = "MyCallerContract",
                    project = "e2e/sway/contracts/low_level_caller"
                ),
                Contract(
                    name = "MyTargetContract",
                    project = "e2e/sway/contracts/contract_test"
                ),
            ),
            Deploy(
                name = "caller_contract_instance",
                contract = "MyCallerContract",
                wallet = "wallet"
            ),
            Deploy(
                name = "target_contract_instance",
                contract = "MyTargetContract",
                wallet = "wallet"
            ),
        );

        // ANCHOR: low_level_call
        let function_selector = encode_fn_selector("set_value_multiple_complex");
        let call_data = calldata!(
            MyStruct {
                a: true,
                b: [1, 2, 3],
            },
            SizedAsciiString::<4>::try_from("fuel")?
        )?;

        caller_contract_instance
            .methods()
            .call_low_level_call(
                target_contract_instance.id(),
                Bytes(function_selector),
                Bytes(call_data),
            )
            .determine_missing_contracts(None)
            .await?
            .call()
            .await?;
        // ANCHOR_END: low_level_call

        let result_uint = target_contract_instance
            .methods()
            .get_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_bool = target_contract_instance
            .methods()
            .get_bool_value()
            .call()
            .await
            .unwrap()
            .value;

        let result_str = target_contract_instance
            .methods()
            .get_str_value()
            .call()
            .await
            .unwrap()
            .value;

        assert_eq!(result_uint, 2);
        assert!(result_bool);
        assert_eq!(result_str, "fuel");

        Ok(())
    }

    #[tokio::test]
    async fn configure_the_return_value_decoder() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_decoder_config
        let _ = contract_instance
            .methods()
            .initialize_counter(42)
            .with_decoder_config(DecoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .call()
            .await?;
        // ANCHOR_END: contract_decoder_config

        Ok(())
    }

    #[tokio::test]
    async fn storage_slots_override() -> Result<()> {
        {
            // ANCHOR: storage_slots_override
            use fuels::{programs::contract::Contract, tx::StorageSlot};
            let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
            let storage_config =
                StorageConfiguration::default().add_slot_overrides([slot_override]);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_override
        }

        {
            // ANCHOR: storage_slots_disable_autoload
            use fuels::programs::contract::Contract;
            let storage_config = StorageConfiguration::default().with_autoload(false);

            let load_config =
                LoadConfiguration::default().with_storage_configuration(storage_config);
            let _: Result<_> = Contract::load_from("...", load_config);
            // ANCHOR_END: storage_slots_disable_autoload
        }

        Ok(())
    }

    #[tokio::test]
    async fn contract_custom_call() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "TestContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "TestContract",
                wallet = "wallet"
            ),
        );
        let provider = wallet.try_provider()?;

        let counter = 42;

        // ANCHOR: contract_call_tb
        let call_handler = contract_instance.methods().initialize_counter(counter);

        let mut tb = call_handler.transaction_builder().await?;

        // customize the builder...

        wallet.adjust_for_fee(&mut tb, 0).await?;
        tb.add_signer(wallet.clone())?;

        let tx = tb.build(provider).await?;

        let tx_id = provider.send_transaction(tx).await?;
        tokio::time::sleep(Duration::from_millis(500)).await;

        let tx_status = provider.tx_status(&tx_id).await?;

        let response = call_handler.get_response_from(tx_status)?;

        assert_eq!(counter, response.value);
        // ANCHOR_END: contract_call_tb

        Ok(())
    }

    #[tokio::test]
    async fn configure_encoder_config() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Wallets("wallet"),
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        // ANCHOR: contract_encoder_config
        let _ = contract_instance
            .with_encoder_config(EncoderConfig {
                max_depth: 10,
                max_tokens: 2_000,
            })
            .methods()
            .initialize_counter(42)
            .call()
            .await?;
        // ANCHOR_END: contract_encoder_config

        Ok(())
    }

    #[tokio::test]
    async fn contract_call_impersonation() -> Result<()> {
        use std::str::FromStr;

        use fuels::prelude::*;

        abigen!(Contract(
            name = "MyContract",
            abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
        ));

        let node_config = NodeConfig {
            utxo_validation: false,
            ..Default::default()
        };
        let mut wallet = WalletUnlocked::new_from_private_key(
            SecretKey::from_str(
                "0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32",
            )?,
            None,
        );
        let coins = setup_single_asset_coins(
            wallet.address(),
            AssetId::zeroed(),
            DEFAULT_NUM_COINS,
            DEFAULT_COIN_AMOUNT,
        );
        let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;
        wallet.set_provider(provider.clone());

        let contract_id = Contract::load_from(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test.bin",
            LoadConfiguration::default(),
        )?
        .deploy(&wallet, TxPolicies::default())
        .await?;

        // ANCHOR: contract_call_impersonation
        // create impersonator for an address
        let address =
            Address::from_str("0x17f46f562778f4bb5fe368eeae4985197db51d80c83494ea7f84c530172dedd1")
                .unwrap();
        let address = Bech32Address::from(address);
        let impersonator = ImpersonatedAccount::new(address, Some(provider.clone()));

        let contract_instance = MyContract::new(contract_id, impersonator.clone());

        let response = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?;

        assert_eq!(42, response.value);
        // ANCHOR_END: contract_call_impersonation

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn deploying_via_loader() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/huge_contract"
            )),
            Wallets("main_wallet")
        );
        let contract_binary =
            "../../e2e/sway/contracts/huge_contract/out/release/huge_contract.bin";

        let provider: Provider = main_wallet.try_provider()?.clone();

        let random_salt = || Salt::new(rand::thread_rng().gen());
        // ANCHOR: show_contract_is_too_big
        let contract = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?;
        let max_allowed = provider
            .consensus_parameters()
            .await?
            .contract_params()
            .contract_max_size();

        assert!(contract.code().len() as u64 > max_allowed);
        // ANCHOR_END: show_contract_is_too_big

        let wallet = main_wallet.clone();

        // ANCHOR: manual_blob_upload_then_deploy
        let max_words_per_blob = 10_000;
        let blobs = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .blobs()
        .to_vec();

        let mut all_blob_ids = vec![];
        let mut already_uploaded_blobs = HashSet::new();
        for blob in blobs {
            let blob_id = blob.id();
            all_blob_ids.push(blob_id);

            // uploading the same blob twice is not allowed
            if already_uploaded_blobs.contains(&blob_id) {
                continue;
            }

            let mut tb = BlobTransactionBuilder::default().with_blob(blob);
            wallet.adjust_for_fee(&mut tb, 0).await?;
            wallet.add_witnesses(&mut tb)?;

            let tx = tb.build(&provider).await?;
            provider
                .send_transaction_and_await_commit(tx)
                .await?
                .check(None)?;

            already_uploaded_blobs.insert(blob_id);
        }

        let contract_id = Contract::loader_from_blob_ids(all_blob_ids, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blob_upload_then_deploy

        // ANCHOR: deploy_via_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: deploy_via_loader

        // ANCHOR: auto_convert_to_loader
        let max_words_per_blob = 10_000;
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
        .await?;
        // ANCHOR_END: auto_convert_to_loader

        // ANCHOR: upload_blobs_then_deploy
        let contract_id = Contract::load_from(
            contract_binary,
            LoadConfiguration::default().with_salt(random_salt()),
        )?
        .convert_to_loader(max_words_per_blob)?
        .upload_blobs(&wallet, TxPolicies::default())
        .await?
        .deploy(&wallet, TxPolicies::default())
        .await?;
        // ANCHOR_END: upload_blobs_then_deploy

        let wallet = main_wallet.clone();
        // ANCHOR: use_loader
        let contract_instance = MyContract::new(contract_id, wallet);
        let response = contract_instance.methods().something().call().await?.value;
        assert_eq!(response, 1001);
        // ANCHOR_END: use_loader

        // ANCHOR: show_max_tx_size
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_size();
        // ANCHOR_END: show_max_tx_size

        // ANCHOR: show_max_tx_gas
        provider
            .consensus_parameters()
            .await?
            .tx_params()
            .max_gas_per_tx();
        // ANCHOR_END: show_max_tx_gas

        let wallet = main_wallet;
        // ANCHOR: manual_blobs_then_deploy
        let chunk_size = 100_000;
        assert!(
            chunk_size % 8 == 0,
            "all chunks, except the last, must be word-aligned"
        );
        let blobs = contract
            .code()
            .chunks(chunk_size)
            .map(|chunk| Blob::new(chunk.to_vec()))
            .collect();

        let contract_id = Contract::loader_from_blobs(blobs, random_salt(), vec![])?
            .deploy(&wallet, TxPolicies::default())
            .await?;
        // ANCHOR_END: manual_blobs_then_deploy

        // ANCHOR: estimate_max_blob_size
        let max_blob_size = BlobTransactionBuilder::default()
            .estimate_max_blob_size(&provider)
            .await?;
        // ANCHOR_END: estimate_max_blob_size

        Ok(())
    }

    #[tokio::test]
    #[allow(unused_variables)]
    async fn decoding_script_transactions() -> Result<()> {
        use fuels::prelude::*;

        setup_program_test!(
            Abigen(Contract(
                name = "MyContract",
                project = "e2e/sway/contracts/contract_test"
            )),
            Wallets("wallet"),
            Deploy(
                name = "contract_instance",
                contract = "MyContract",
                wallet = "wallet"
            )
        );

        let tx_id = contract_instance
            .methods()
            .initialize_counter(42)
            .call()
            .await?
            .tx_id
            .unwrap();

        let provider: &Provider = wallet.try_provider()?;

        // ANCHOR: decoding_script_transactions
        let TransactionType::Script(tx) = provider
            .get_transaction_by_id(&tx_id)
            .await?
            .unwrap()
            .transaction
        else {
            panic!("Transaction is not a script transaction");
        };

        let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
        else {
            panic!("Script is not a contract call");
        };

        let json_abi = std::fs::read_to_string(
            "../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json",
        )?;
        let abi_formatter = ABIFormatter::from_json_abi(json_abi)?;

        let call = &calls[0];
        let fn_selector = call.decode_fn_selector()?;
        let decoded_args =
            abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

        eprintln!(
            "The script called: {fn_selector}({})",
            decoded_args.join(", ")
        );

        // ANCHOR_END: decoding_script_transactions
        Ok(())
    }
}\n```

prints:

The script called: initialize_counter(42)

The AbiFormatter can also decode configurables, refer to the rust docs for more information.

Glossary

Contract

A contract, in the SDK, is an abstraction that represents a connection to a specific smart contract deployed on the Fuel Network. This contract instance can be used as a regular Rust object, with methods attached to it that reflect those in its smart contract equivalent.

Provider

A Provider is a struct that provides an abstraction for a connection to a Fuel node. It provides read-only access to the node. You can use this provider as-is or through the wallet.

Wallet and signer

A Wallet is a struct with direct or indirect access to a private key. You can use a Wallet to sign messages and transactions to authorize the network to charge your account to perform operations. The terms wallet and signer in the SDK are often used interchangeably, but, technically, a Signer is simply a Rust trait to enable the signing of transactions and messages; the Wallet implements the Signer trait.

Contributing to the Fuel Rust SDK

Thanks for your interest in contributing to the Fuel Rust SDK!

This document outlines the process for installing dependencies, setting up for development, and conventions for contributing.`

If you run into any difficulties getting started, you can always ask questions on our Discourse.

Finding something to work on

You may contribute to the project in many ways, some of which involve coding knowledge and some which do not. A few examples include:

  • Reporting bugs
  • Adding new features or bug fixes for which there is already an open issue
  • Making feature requests

Check out our Help Wanted or Good First Issues to find a suitable task.

If you are planning something big, for example, changes related to multiple components or changes to current behaviors, make sure to open an issue to discuss with us before starting on the implementation.

Contribution flow

This is a rough outline of what a contributor's workflow looks like:

  • Make sure what you want to contribute is already tracked as an issue.
    • We may discuss the problem and solution in the issue.
  • Create a Git branch from where you want to base your work. This is usually master.
  • Write code, add test cases, and commit your work.
  • Run tests and make sure all tests pass.
  • Add the breaking label to your PR if the PR contains any breaking changes.
  • Push your changes to a branch in your fork of the repository and submit a pull request.
    • Make sure to mention the issue created in step 1 in the commit message.
  • Your PR will be reviewed, and some changes may be requested.
    • Your PR must be re-reviewed and approved once you've made changes.
    • Use GitHub's 'update branch' button if the PR becomes outdated.
    • If there are conflicts, you can merge and resolve them locally. Then push to your PR branch. Any changes to the branch will require a re-review.
  • Our CI system (Github Actions) automatically tests all authorized pull requests.
  • Use GitHub to merge the PR once approved.

Thanks for your contributions!

Linking issues

Pull requests should be linked to at least one issue in the same repo.

If the pull request resolves the relevant issues, and you want GitHub to close these issues automatically after it merged into the default branch, you can use the syntax (KEYWORD #ISSUE-NUMBER) like this:

close #123

If the pull request links an issue but does not close it, you can use the keyword ref like this:

ref #456

Multiple issues should use full syntax for each issue and be separated by a comma, like:

close #123, ref #456

Integration tests structure in fuels-rs

The integration tests of fuels-rs cover almost all aspects of the SDK and have grown significantly as more functionality was added. To make the tests and associated Sway projects more manageable they were split into several categories. A category consist of a .rs file for the tests and, if needed, a separate directory for the Sway projects.

Currently have the following structure:

  .
  ├─  bindings/
  ├─  contracts/
  ├─  logs/
  ├─  predicates/
  ├─  storage/
  ├─  types/
  ├─  bindings.rs
  ├─  contracts.rs
  ├─  from_token.rs
  ├─  logs.rs
  ├─  predicates.rs
  ├─  providers.rs
  ├─  scripts.rs
  ├─  storage.rs
  ├─  types.rs
  └─  wallets.rs

Even though test organization is subjective, please consider these guidelines before adding a new category:

  • Add a new category when creating a new section in the Fuels Rust SDK book - e.g. Types
  • Add a new category if there are more than 3 test and more than 100 lines of code and they form a group of tests - e.g. storage.rs

Otherwise, we recommend putting the integration test inside the existing categories above.

fuels-rs Rust Workspaces

This section gives you a little overview of the role and function of every workspace in the fuels-rs repository.

fuels-abi-cli

Simple CLI program to encode Sway function calls and decode their output. The ABI being encoded and decoded is specified here.

Usage

sway-abi-cli 0.1.0
FuelVM ABI coder

USAGE:
    sway-abi-cli <SUBCOMMAND>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

SUBCOMMANDS:
    codegen   Output Rust types file
    decode    Decode ABI call result
    encode    Encode ABI call
    help      Prints this message or the help of the given subcommand(s)

Examples

You can choose to encode only the given params or you can go a step further and have a full JSON ABI file and encode the whole input to a certain function call defined in the JSON file.

Encoding params only

$ cargo run -- encode params -v bool true
0000000000000001
$ cargo run -- encode params -v bool true -v u32 42 -v u32 100
0000000000000001000000000000002a0000000000000064

Note that for every parameter you want to encode, you must pass a -v flag followed by the type, and then the value: -v <type_1> <value_1> -v <type_2> <value_2> -v <type_n> <value_n>

Encoding function call

example/simple.json:

[
  {
    "type":"function",
    "inputs":[
      {
        "name":"arg",
        "type":"u32"
      }
    ],
    "name":"takes_u32_returns_bool",
    "outputs":[
      {
        "name":"",
        "type":"bool"
      }
    ]
  }
]
$ cargo run -- encode function examples/simple.json takes_u32_returns_bool -p 4
000000006355e6ee0000000000000004

example/array.json

[
  {
    "type":"function",
    "inputs":[
      {
        "name":"arg",
        "type":"u16[3]"
      }
    ],
    "name":"takes_array",
    "outputs":[
      {
        "name":"",
        "type":"u16[2]"
      }
    ]
  }
]
$ cargo run -- encode function examples/array.json takes_array -p '[1,2]'
00000000f0b8786400000000000000010000000000000002

Note that the first word (8 bytes) of the output is reserved for the function selector, which is captured in the last 4 bytes, which is simply the 256hash of the function signature.

Example with nested struct:

[
  {
    "type":"contract",
    "inputs":[
      {
        "name":"MyNestedStruct",
        "type":"struct",
        "components":[
          {
            "name":"x",
            "type":"u16"
          },
          {
            "name":"y",
            "type":"struct",
            "components":[
              {
                "name":"a",
                "type":"bool"
              },
              {
                "name":"b",
                "type":"u8[2]"
              }
            ]
          }
        ]
      }
    ],
    "name":"takes_nested_struct",
    "outputs":[

    ]
  }
]
$ cargo run -- encode function examples/nested_struct.json takes_nested_struct -p '(10, (true, [1,2]))'
00000000e8a04d9c000000000000000a000000000000000100000000000000010000000000000002

Decoding params only

Similar to encoding parameters only:

$ cargo run -- decode params -t bool -t u32 -t u32 0000000000000001000000000000002a0000000000000064
Bool(true)
U32(42)
U32(100)

Decoding function output

$ cargo run -- decode function examples/simple.json takes_u32_returns_bool 0000000000000001
Bool(true)

Transaction Format

The Fuel Transaction Format.

Consensus Parameters

nametypedescription
GAS_PER_BYTEuint64Gas charged per byte of the transaction.
GAS_PRICE_FACTORuint64Unit factor for gas price.
MAX_GAS_PER_TXuint64Maximum gas per transaction.
MAX_INPUTSuint64Maximum number of inputs.
MAX_OUTPUTSuint64Maximum number of outputs.
MAX_PREDICATE_LENGTHuint64Maximum length of predicate, in instructions.
MAX_GAS_PER_PREDICATEuint64Maximum gas per predicate.
MAX_PREDICATE_DATA_LENGTHuint64Maximum length of predicate data, in bytes.
MAX_SCRIPT_LENGTHuint64Maximum length of script, in instructions.
MAX_SCRIPT_DATA_LENGTHuint64Maximum length of script data, in bytes.
MAX_MESSAGE_DATA_LENGTHuint64Maximum length of message data, in bytes.
MAX_STORAGE_SLOTSuint64Maximum number of initial storage slots.
MAX_TRANSACTION_SIZEuint64Maximum size of a transaction, in bytes.
MAX_WITNESSESuint64Maximum number of witnesses.
MAX_BYTECODE_SUBSECTIONSuint64Maximum number of bytecode subsections.
CHAIN_IDuint64A unique per-chain identifier.
BASE_ASSET_IDbytes32The base asset of the chain.
PRIVILEGED_ADDRESSbytes32The privileged address of the network who can perform upgrade.

Transaction

enum TransactionType : uint8 {
    Script = 0,
    Create = 1,
    Mint = 2,
    Upgrade = 3,
    Upload = 4,
    Blob = 5,
}
nametypedescription
typeTransactionTypeTransaction type.
dataOne of TransactionScript, TransactionCreate, TransactionMint, TransactionUpgrade, or TransactionUploadTransaction data.

Given helper max_gas() returns the maximum gas that the transaction can use. Given helper count_ones() that returns the number of ones in the binary representation of a field. Given helper count_variants() that returns the number of variants in an enum. Given helper sum_variants() that sums all variants of an enum.

Transaction is invalid if:

  • type is not valid TransactionType value
  • inputsCount > MAX_INPUTS
  • outputsCount > MAX_OUTPUTS
  • witnessesCount > MAX_WITNESSES
  • size > MAX_TRANSACTION_SIZE. The size of a transaction is calculated as the sum of the sizes of its static and dynamic parts, as determined by canonical serialization.
  • No inputs are of type InputType.Coin or InputType.Message with input.dataLength == 0
  • More than one output is of type OutputType.Change for any asset ID in the input set
  • More than one output is of type OutputType.Change with identical asset_id fields.
  • Any output is of type OutputType.Change for any asset ID not in the input set
  • More than one input of type InputType.Coin for any Coin ID in the input set
  • More than one input of type InputType.Contract for any Contract ID in the input set
  • More than one input of type InputType.Message for any Message ID in the input set
  • if type != TransactionType.Mint
    • max_gas(tx) > MAX_GAS_PER_TX
    • No policy of type PolicyType.MaxFee is set
    • count_ones(policyTypes) > count_variants(PolicyType)
    • policyTypes > sum_variants(PolicyType)
    • len(policies) > count_ones(policyTypes)

When serializing a transaction, fields are serialized as follows (with inner structs serialized recursively):

  1. uint8, uint16, uint32, uint64: big-endian right-aligned to 8 bytes.
  2. byte[32]: as-is.
  3. byte[]: as-is, with padding zeroes aligned to 8 bytes.

When deserializing a transaction, the reverse is done. If there are insufficient bytes or too many bytes, the transaction is invalid.

TransactionScript

enum ReceiptType : uint8 {
    Call = 0,
    Return = 1,
    ReturnData = 2,
    Panic = 3,
    Revert = 4,
    Log = 5,
    LogData = 6,
    Transfer = 7,
    TransferOut = 8,
    ScriptResult = 9,
    MessageOut = 10,
    Mint = 11,
    Burn = 12,
}
nametypedescription
scriptGasLimituint64Gas limits the script execution.
receiptsRootbyte[32]Merkle root of receipts.
scriptLengthuint64Script length, in instructions.
scriptDataLengthuint64Length of script input data, in bytes.
policyTypesuint32Bitfield of used policy types.
inputsCountuint16Number of inputs.
outputsCountuint16Number of outputs.
witnessesCountuint16Number of witnesses.
scriptbyte[]Script to execute.
scriptDatabyte[]Script input data (parameters).
policiesPolicy[]List of policies, sorted by PolicyType.
inputsInput[]List of inputs.
outputsOutput[]List of outputs.
witnessesWitness[]List of witnesses.

Given helper len() that returns the number of bytes of a field.

Transaction is invalid if:

  • Any output is of type OutputType.ContractCreated
  • scriptLength > MAX_SCRIPT_LENGTH
  • scriptDataLength > MAX_SCRIPT_DATA_LENGTH
  • scriptLength * 4 != len(script)
  • scriptDataLength != len(scriptData)

Note: when signing a transaction, receiptsRoot is set to zero.

Note: when verifying a predicate or executing a script, receiptsRoot is initialized to zero.

The receipts root receiptsRoot is the root of the binary Merkle tree of receipts. If there are no receipts, its value is set to the root of the empty tree, i.e. 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.

TransactionCreate

nametypedescription
bytecodeWitnessIndexuint16Witness index of contract bytecode to create.
saltbyte[32]Salt.
storageSlotsCountuint64Number of storage slots to initialize.
policyTypesuint32Bitfield of used policy types.
inputsCountuint16Number of inputs.
outputsCountuint16Number of outputs.
witnessesCountuint16Number of witnesses.
storageSlots(byte[32], byte[32]])[]List of storage slots to initialize (key, value).
policiesPolicy[]List of policies.
inputsInput[]List of inputs.
outputsOutput[]List of outputs.
witnessesWitness[]List of witnesses.

Transaction is invalid if:

  • Any input is of type InputType.Contract or InputType.Message where input.dataLength > 0
  • Any input uses non-base asset.
  • Any output is of type OutputType.Contract or OutputType.Variable or OutputType.Message
  • Any output is of type OutputType.Change with non-base asset_id
  • It does not have exactly one output of type OutputType.ContractCreated
  • tx.data.witnesses[bytecodeWitnessIndex].dataLength > CONTRACT_MAX_SIZE
  • bytecodeWitnessIndex >= tx.witnessesCount
  • The keys of storageSlots are not in ascending lexicographic order
  • The computed contract ID (see below) is not equal to the contractID of the one OutputType.ContractCreated output
  • storageSlotsCount > MAX_STORAGE_SLOTS
  • The Sparse Merkle tree root of storageSlots is not equal to the stateRoot of the one OutputType.ContractCreated output

Creates a contract with contract ID as computed here.

TransactionMint

The transaction is created by the block producer and is not signed. Since it is not usable outside of block creation or execution, all fields must be fully set upon creation without any zeroing. This means that the transaction ID must also include the correct txPointer value, not zeroed out.

nametypedescription
txPointerTXPointerThe location of the Mint transaction in the block.
inputContractInputContractThe contract UTXO that assets are minted to.
outputContractOutputContractThe contract UTXO that assets are being minted to.
mintAmountuint64The amount of funds minted.
mintAssetIdbyte[32]The asset IDs corresponding to the minted amount.
gasPriceuint64The gas price to be used in calculating all fees for transactions on block

Transaction is invalid if:

  • txPointer is zero or doesn't match the block.
  • outputContract.inputIndex is not zero

TransactionUpgrade

The Upgrade transaction allows upgrading either consensus parameters or state transition function used by the network to produce future blocks. The Upgrade Purpose type defines the purpose of the upgrade.

The Upgrade transaction is chargeable, and the sender should pay for it. Transaction inputs should contain only base assets.

Only the privileged address from ConsensusParameters can upgrade the network. The privileged address can be either a real account or a predicate.

When the upgrade type is UpgradePurposeType.ConsensusParameters serialized consensus parameters are available in the witnesses and the Upgrade transaction is self-contained because it has all the required information.

When the upgrade type is UpgradePurposeType.StateTransition, the bytecodeRoot field contains the Merkle root of the new bytecode of the state transition function. The bytecode should already be available on the blockchain at the upgrade point; otherwise, the upgrade will fail. The bytecode can be part of the genesis block or can be uploaded via the TransactionUpload transaction.

The block header contains information about which versions of consensus parameters and state transition function are used to produce a block, and the Upgrade transaction defines behavior corresponding to the version. When the block executes the Upgrade transaction, it defines new behavior for either BlockHeader.consensusParametersVersion + 1 or BlockHeader.stateTransitionBytecodeVersion + 1(it depends on the purpose of the upgrade).

When the Upgrade transaction is included in the block, it doesn't affect the current block execution. Since behavior is now defined, the inclusion of the Upgrade transaction allows the production of the next block with a new version. The block producer can still continue to use the previous version and start using a new version later unless the state transition function forbids it.

The behavior is set once per version. It is forbidden to override the behavior of the network. Each behavior should have its own version. The version should grow monotonically without jumps.

The BlockHeader.consensusParametersVersion and BlockHeader.stateTransitionBytecodeVersion are independent and can grow at different speeds.

nametypedescription
upgradePurposeUpgradePurposeThe purpose of the upgrade.
policyTypesuint32Bitfield of used policy types.
inputsCountuint16Number of inputs.
outputsCountuint16Number of outputs.
witnessesCountuint16Number of witnesses.
policiesPolicy[]List of policies.
inputsInput[]List of inputs.
outputsOutput[]List of outputs.
witnessesWitness[]List of witnesses.

Transaction is invalid if:

  • Any input is of type InputType.Contract or InputType.Message where input.dataLength > 0
  • Any input uses non-base asset.
  • Any output is of type OutputType.Contract or OutputType.Variable or OutputType.Message or OutputType.ContractCreated
  • Any output is of type OutputType.Change with non-base asset_id
  • No input where InputType.Message.owner == PRIVILEGED_ADDRESS or InputType.Coint.owner == PRIVILEGED_ADDRESS
  • The UpgradePurpose is invalid

TransactionUpload

The Upload transaction allows the huge bytecode to be divided into subsections and uploaded slowly to the chain. The Binary Merkle root built on top of subsections is an identifier of the bytecode.

Each transaction uploads a subsection of the code and must contain proof of connection to the root. All subsections should be uploaded sequentially, which allows the concatenation of previously uploaded subsections with new subsection. The bytecode is considered final when the last subsection is uploaded, and future Upload transactions with the same root fields should be rejected.

When the bytecode is completed it can be used to upgrade the network.

The size of each subsection can be arbitrary; the only limit is the maximum number of subsections allowed by the network. The combination of the transaction gas limit and the number of subsections limits the final maximum size of the bytecode.

nametypedescription
rootbyte[32]The root of the Merkle tree is created over the bytecode.
witnessIndexuint16The witness index of the subsection of the bytecode.
subsectionIndexuint16The index of the subsection of the bytecode.
subsectionsNumberuint16The total number of subsections on which bytecode was divided.
proofSetCountuint16Number of Merkle nodes in the proof.
policyTypesuint32Bitfield of used policy types.
inputsCountuint16Number of inputs.
outputsCountuint16Number of outputs.
witnessesCountuint16Number of witnesses.
proofSetbyte[32][]The proof set of Merkle nodes to verify the connection of the subsection to the root.
policiesPolicy[]List of policies.
inputsInput[]List of inputs.
outputsOutput[]List of outputs.
witnessesWitness[]List of witnesses.

Transaction is invalid if:

  • Any input is of type InputType.Contract or InputType.Message where input.dataLength > 0
  • Any input uses non-base asset.
  • Any output is of type OutputType.Contract or OutputType.Variable or OutputType.Message or OutputType.ContractCreated
  • Any output is of type OutputType.Change with non-base asset_id
  • witnessIndex >= tx.witnessesCount
  • subsectionIndex >= subsectionsNumber
  • subsectionsNumber > MAX_BYTECODE_SUBSECTIONS
  • The Binary Merkle tree root calculated from (witnesses[witnessIndex], subsectionIndex, subsectionsNumber, proofSet) is not equal to the root. Root calculation is affected by all fields, so modification of one of them invalidates the proof.

TransactionBlob

The Blob inserts a simple binary blob in the chain. It's raw immutable data that can be cheaply loaded by the VM and used as instructions or just data. Unlike Create, it doesn't hold any state or balances.

Blobs are content-addressed, i.e. the they are uniquely identified by hash of the data field. Programs running on the VM can load an already-posted blob just by the hash, without having to specify it in contract inputs.

nametypedescription
idbyte[32]Blob id, i.e. hash of the data.
witnessIndexuint16The witness index of the data.
policyTypesuint32Bitfield of used policy types.
inputsCountuint16Number of inputs.
outputsCountuint16Number of outputs.
witnessesCountuint16Number of witnesses.
policiesPolicy[]List of policies.
inputsInput[]List of inputs.
outputsOutput[]List of outputs.
witnessesWitness[]List of witnesses.

Transaction is invalid if:

  • Any input is of type InputType.Contract or InputType.Message where input.dataLength > 0
  • Any input uses non-base asset.
  • Any output is of type OutputType.Contract or OutputType.Variable or OutputType.Message or OutputType.ContractCreated
  • Any output is of type OutputType.Change with non-base asset_id
  • witnessIndex >= tx.witnessesCount
  • sha256(witnesses[witnessIndex]) != id

UpgradePurposeType

enum UpgradePurposeType : uint8 {
    ConsensusParameters = 0,
    StateTransition = 1,
}
nametypedescription
typeUpgradePurposeTypeType of upgrade purpose.
dataOne of ConsensusParameters, StateTransitionUpgrade purposes.

Transaction is invalid if:

  • type is not valid UpgradePurposeType value`

ConsensusParameters

nametypedescription
witnessIndexuint16Index of witness that contains a serialized(with postcard) consensus parameters.
checksumbyte[32]The hash of the serialized consensus parameters.

Given helper deserialize_consensus_parameters() that deserializes the consensus parameters from a witness by using postcard algorithm.

Transaction is invalid if:

  • witnessIndex >= tx.witnessesCount
  • checksum != sha256(tx.data.witnesses[witnessIndex])
  • deserialize_consensus_parameters(tx.data.witnesses[witnessIndex]) returns an error.

StateTransition

nametypedescription
bytecodeRootbyte[32]The root of the new bytecode of the state transition function.

Policy

// index using powers of 2 for efficient bitmasking
enum PolicyType : uint32 {
    Tip = 1,
    WitnessLimit = 2,
    Maturity = 4,
    MaxFee = 8,
}
nametypedescription
dataOne of Tip, WitnessLimit, or MaturityPolicy data.

Tip

nametypedescription
tipuint64Additional, optional fee in BASE_ASSET to incentivize block producer to include transaction

WitnessLimit

nametypedescription
witnessLimituint64The maximum amount of witness data allowed for the transaction

Given helper len() that returns the number of bytes of a field.

Transaction is invalid if:

  • len(tx.witnesses) > witnessLimit

Maturity

nametypedescription
maturityuint32Block until which the transaction cannot be included.

Transaction is invalid if:

  • blockheight() < maturity

MaxFee

nametypedescription
max_feeuint64Required policy to specify the maximum fee payable by this transaction using BASE_ASSET. This is used to check transactions before the actual gas_price is known.

Transaction is invalid if:

  • max_fee > sum_inputs(tx, BASE_ASSET_ID) - sum_outputs(tx, BASE_ASSET_ID)
  • max_fee < max_fee(tx, BASE_ASSET_ID, gas_price)

Input

enum InputType : uint8 {
    Coin = 0,
    Contract = 1,
    Message = 2,
}
nametypedescription
typeInputTypeType of input.
dataOne of InputCoin, InputContract, or InputMessageInput data.

Transaction is invalid if:

  • type > InputType.Message

InputCoin

nametypedescription
txIDbyte[32]Hash of transaction.
outputIndexuint16Index of transaction output.
ownerbyte[32]Owning address or predicate root.
amountuint64Amount of coins.
asset_idbyte[32]Asset ID of the coins.
txPointerTXPointerPoints to the TX whose output is being spent.
witnessIndexuint16Index of witness that authorizes spending the coin.
predicateGasUseduint64Gas used by predicate.
predicateLengthuint64Length of predicate, in instructions.
predicateDataLengthuint64Length of predicate input data, in bytes.
predicatebyte[]Predicate bytecode.
predicateDatabyte[]Predicate input data (parameters).

Given helper len() that returns the number of bytes of a field.

Transaction is invalid if:

  • witnessIndex >= tx.witnessesCount
  • predicateLength > MAX_PREDICATE_LENGTH
  • predicateDataLength > MAX_PREDICATE_DATA_LENGTH
  • If predicateLength > 0; the computed predicate root (see below) is not equal owner
  • predicateLength * 4 != len(predicate)
  • predicateDataLength != len(predicateData)
  • predicateGasUsed > MAX_GAS_PER_PREDICATE

Note: when signing a transaction, txPointer and predicateGasUsed are set to zero.

Note: when verifying and estimating a predicate or executing a script, txPointer and predicateGasUsed are initialized to zero.

The predicate root is computed here.

InputContract

nametypedescription
txIDbyte[32]Hash of transaction.
outputIndexuint16Index of transaction output.
balanceRootbyte[32]Root of amount of coins owned by contract before transaction execution.
stateRootbyte[32]State root of contract before transaction execution.
txPointerTXPointerPoints to the TX whose output is being spent.
contractIDbyte[32]Contract ID.

Transaction is invalid if:

  • there is not exactly one output of type OutputType.Contract with inputIndex equal to this input's index

Note: when signing a transaction, txID, outputIndex, balanceRoot, stateRoot, and txPointer are set to zero.

Note: when verifying a predicate or executing a script, txID, outputIndex, balanceRoot, stateRoot, and txPointer are initialized to zero.

InputMessage

nametypedescription
senderbyte[32]The address of the message sender.
recipientbyte[32]The address or predicate root of the message recipient.
amountuint64Amount of base asset coins sent with message.
noncebyte[32]The message nonce.
witnessIndexuint16Index of witness that authorizes spending the coin.
predicateGasUseduint64Gas used by predicate execution.
dataLengthuint64Length of message data, in bytes.
predicateLengthuint64Length of predicate, in instructions.
predicateDataLengthuint64Length of predicate input data, in bytes.
databyte[]The message data.
predicatebyte[]Predicate bytecode.
predicateDatabyte[]Predicate input data (parameters).

Given helper len() that returns the number of bytes of a field.

Transaction is invalid if:

  • witnessIndex >= tx.witnessesCount
  • dataLength > MAX_MESSAGE_DATA_LENGTH
  • predicateLength > MAX_PREDICATE_LENGTH
  • predicateDataLength > MAX_PREDICATE_DATA_LENGTH
  • If predicateLength > 0; the computed predicate root (see below) is not equal recipient
  • dataLength != len(data)
  • predicateLength * 4 != len(predicate)
  • predicateDataLength != len(predicateData)
  • predicateGasUsed > MAX_GAS_PER_PREDICATE

The predicate root is computed here.

Note: InputMessages with data length greater than zero are not considered spent until they are included in a transaction of type TransactionType.Script with a ScriptResult receipt where result is equal to 0 indicating a successful script exit

Note: when signing a transaction, predicateGasUsed is set to zero.

Note: when verifying and estimating a predicate, predicateGasUsed is initialized to zero.

Output

enum OutputType : uint8 {
    Coin = 0,
    Contract = 1,
    Change = 2,
    Variable = 3,
    ContractCreated = 4,
}
nametypedescription
typeOutputTypeType of output.
dataOne of OutputCoin, OutputContract, OutputChange, OutputVariable, or OutputContractCreated.Output data.

Transaction is invalid if:

  • type > OutputType.ContractCreated

OutputCoin

nametypedescription
tobyte[32]Receiving address or predicate root.
amountuint64Amount of coins to send.
asset_idbyte[32]Asset ID of coins.

OutputContract

nametypedescription
inputIndexuint16Index of input contract.
balanceRootbyte[32]Root of amount of coins owned by contract after transaction execution.
stateRootbyte[32]State root of contract after transaction execution.

Transaction is invalid if:

  • inputIndex >= tx.inputsCount
  • tx.inputs[inputIndex].type != InputType.Contract

Note: when signing a transaction, balanceRoot and stateRoot are set to zero.

Note: when verifying a predicate or executing a script, balanceRoot and stateRoot are initialized to zero.

The balance root balanceRoot is the root of the SMT of balance leaves. Each balance is a uint64, keyed by asset ID (a byte[32]).

The state root stateRoot is the root of the SMT of storage slots. Each storage slot is a byte[32], keyed by a byte[32].

OutputChange

nametypedescription
tobyte[32]Receiving address or predicate root.
amountuint64Amount of coins to send.
asset_idbyte[32]Asset ID of coins.

Transaction is invalid if:

  • any other output has type OutputType.OutputChange and asset ID asset_id (i.e. only one change output per asset ID is allowed)

Note: when signing a transaction, amount is set to zero.

Note: when verifying a predicate or executing a script, amount is initialized to zero.

This output type indicates that the output's amount may vary based on transaction execution, but is otherwise identical to a Coin output. An amount of zero after transaction execution indicates that the output is unspendable and can be pruned from the UTXO set.

OutputVariable

nametypedescription
tobyte[32]Receiving address or predicate root.
amountuint64Amount of coins to send.
asset_idbyte[32]Asset ID of coins.

Note: when signing a transaction, to, amount, and asset_id are set to zero.

Note: when verifying a predicate or executing a script, to, amount, and asset_id are initialized to zero.

This output type indicates that the output's amount and owner may vary based on transaction execution, but is otherwise identical to a Coin output. An amount of zero after transaction execution indicates that the output is unspendable and can be pruned from the UTXO set.

OutputContractCreated

nametypedescription
contractIDbyte[32]Contract ID.
stateRootbyte[32]Initial state root of contract.

Witness

nametypedescription
dataLengthuint64Length of witness data, in bytes.
databyte[]Witness data.

TXPointer

The location of the transaction in the block. It can be used by UTXOs as a reference to the transaction or by the transaction itself to make it unique.

nametypedescription
blockHeightuint32Block height.
txIndexuint16Transaction index.

Identifiers

This chapter defines how to compute unique identifiers.

Asset ID

The asset ID (also called asset hash) of a asset is computed as the hash of the CONTRACT_ID and a 256-bit SUB_IDENTIFIER.

sha256(CONTRACT_ID ++ SUB_IDENTIFIER)

Blob ID

The blob ID (also called blob hash) of a transaction is computed as the hash of the blob data.

Blob ID calculation doesn't vary between chains.

sha256(blob_data)

Contract ID

For a transaction of type TransactionType.Create, tx, the contract ID is sha256(0x4655454C ++ tx.data.salt ++ root(tx.data.witnesses[bytecodeWitnessIndex].data) ++ root_smt(tx.storageSlots)), where root is the Merkle root of the binary Merkle tree with each leaf being 16KiB of instructions, and root_smt is the Sparse Merkle tree root of the provided key-value pairs. If the bytecode is not a multiple of 16 KiB, the final leaf should be zero-padded rounding up to the nearest multiple of 8 bytes.

Predicate ID

For an input of type InputType.Coin or InputType.Message, input, the predicate owner is calculated as: sha256(0x4655454C ++ root(input.predicate)), where root is the Merkle root of the binary Merkle tree each leaf being 16KiB of instructions. If the bytecode is not a multiple of 16 KiB, the final leaf should be zero-padded rounding up to the nearest multiple of 8 bytes.

Transaction ID

The transaction ID (also called transaction hash) of a transaction is computed as the hash of CHAIN_ID and the serialized transaction with fields zeroed out for signing (see different inputs and outputs for which fields are set to zero), and without witness data. In other words, only all non-witness data is hashed.

sha256(CHAIN_ID ++ serialized_tx(tx))

UTXO ID

Coin ID

Is represented as an outpoint: a pair of transaction ID as byte[32] and output index as a uint16.

Message ID

The ID of a message is computed as the hash of:

  1. the sender address as byte[32],
  2. the recipient address as byte[32],
  3. the Message nonce as byte[32],
  4. the amount being sent with the message as uint64,
  5. the message data as byte[]

hash(byte[32] ++ byte[32] ++ byte[32] ++ uint64 ++ byte[]). The address values are serialized as a byte array of length 32 left-padded with zeroes, and all other value types are serialized according to the standard transaction serialization. Note that the message data length is not included since there is only one dynamically sized field and can be implicitly determined by the hash preimage size.

Message Nonce

The nonce value for InputMessage is determined by the sending system and is published at the time the message is sent. The nonce value for OutputMessage is computed as the hash of the Transaction ID that emitted the message and the index of the message receipt uint16 (with canonical encoding): hash(byte[32] ++ canonical(uint16)).

Fee ID

The UTXO ID of collected fees in a block is the block height as a 32-byte big-endian unsigned integer (i.e. the first byte of the 32-byte array is the most significant byte, and so on).

Protocol

Transaction Validity

Transaction Life Cycle

Once a transaction is seen, it goes through several stages of validation, in this order:

  1. Pre-checks
  2. Predicate verification
  3. Script execution
  4. Post-checks

Access Lists

The validity rules below assume sequential transaction validation for side effects (i.e. state changes). However, by construction, transactions with disjoint write access lists can be validated in parallel, including with overlapping read-only access lists. Transactions with overlapping write access lists must be validated and placed in blocks in topological order.

UTXOs and contracts in the read-only and write-destroy access lists must exist (i.e. have been created previously) in order for a transaction to be valid. In other words, for a unique state element ID, the write-create must precede the write-destroy.

Read-only access list:

Write-destroy access list:

Write-create access list:

Note that block proposers use the contract ID contractID for inputs and outputs of type InputType.Contract and OutputType.Contract rather than the pair of txId and outputIndex.

VM Precondition Validity Rules

This section defines VM precondition validity rules for transactions: the bare minimum required to accept an unconfirmed transaction into a mempool, and preconditions that the VM assumes to hold prior to execution. Chains of unconfirmed transactions are omitted.

For a transaction tx, UTXO set state, contract set contracts, and message set messages, the following checks must pass.

Note: InputMessages where input.dataLength > 0 are not dropped from the messages message set until they are included in a transaction of type TransactionType.Script with a ScriptResult receipt where result is equal to 0 indicating a successful script exit.

Base Sanity Checks

Base sanity checks are defined in the transaction format.

Spending UTXOs and Created Contracts

for input in tx.inputs:
    if input.type == InputType.Contract:
        if not input.contractID in contracts:
                return False
    elif input.type == InputType.Message:
        if not input.nonce in messages:
                return False
    else:
        if not (input.txId, input.outputIndex) in state:
            return False
return True

If this check passes, the UTXO ID (txId, outputIndex) fields of each contract input is set to the UTXO ID of the respective contract. The txPointer of each input is also set to the TX pointer of the UTXO with ID utxoID.

Sufficient Balance

For each asset ID assetId in the input and output set:

def gas_to_fee(gas, gasPrice) -> int:
    """
    Converts gas units into a fee amount
    """
    return ceil(gas * gasPrice / GAS_PRICE_FACTOR)


def sum_data_messages(tx, assetId) -> int:
    """
    Returns the total balance available from messages containing data
    """
    total: int = 0
    if assetId == 0:
        for input in tx.inputs:
            if input.type == InputType.Message and input.dataLength > 0:
                total += input.amount
    return total


def sum_inputs(tx, assetId) -> int:
    total: int = 0
    for input in tx.inputs:
        if input.type == InputType.Coin and input.assetId == assetId:
            total += input.amount
        elif input.type == InputType.Message and assetId == 0 and input.dataLength == 0:
            total += input.amount
    return total


def transaction_size_gas_fees(tx) -> int:
    """
    Computes the intrinsic gas cost of a transaction based on size in bytes
    """
    return size(tx) * GAS_PER_BYTE


def minted(tx, assetId) -> int:
    """
    Returns any minted amounts by the transaction
    """
    if tx.type != TransactionType.Mint or assetId != tx.mintAssetId:
        return 0
    return tx.mint_amount


def sum_outputs(tx, assetId) -> int:
    total: int = 0
    for output in tx.outputs:
        if output.type == OutputType.Coin and output.assetId == assetId:
            total += output.amount
    return total


def input_gas_fees(tx) -> int:
    """
    Computes the intrinsic gas cost of verifying input utxos
    """
    total: int = 0
    witnessIndices = set()
    for input in tx.inputs:
        if input.type == InputType.Coin or input.type == InputType.Message:
            # add fees allocated for predicate execution
            if input.predicateLength == 0:
                # notate witness index if input is signed
                witnessIndices.add(input.witnessIndex)
            else:
                # add intrinsic gas cost of predicate merkleization based on number of predicate bytes
                total += contract_code_root_gas_fee(input.predicateLength)
                total += input.predicateGasUsed
                # add intrinsic cost of vm initialization
                total += vm_initialization_gas_fee()
    # add intrinsic cost of verifying witness signatures
    total += len(witnessIndices) * eck1_recover_gas_fee()
    return total


def metadata_gas_fees(tx) -> int:
    """
    Computes the intrinsic gas cost of processing transaction outputs
    
    The `contract_code_root_gas_fee`, `sha256_gas_fee`, and `contract_state_root_gas_fee` 
    are based on the benchmarked gas costs of these operations.
    
    Consensus parameters contain definitions of gas costs for all operations and opcodes in the network.
    """
    total: int = 0
    if tx.type == TransactionType.Create:
        for output in tx.outputs:
            if output.type == OutputType.OutputContractCreated:
                # add intrinsic cost of calculating the code root based on the size of the contract bytecode
                total += contract_code_root_gas_fee(tx.witnesses[tx.bytecodeWitnessIndex].dataLength)
                # add intrinsic cost of calculating the state root based on the number of sotrage slots
                total += contract_state_root_gas_fee(tx.storageSlotCount)
                # add intrinsic cost of calculating the contract id 
                # size = 4 byte seed + 32 byte salt + 32 byte code root + 32 byte state root
                total += sha256_gas_fee(100)
    elif tx.type == TransactionType.Upgrade:
        if tx.upgradePurpose.type == UpgradePurposeType.ConsensusParameters:
            # add intrinsic cost of calculating the consensus parameters hash
            total += sha256_gas_fee(size(tx.witnesses[tx.upgradePurpose.witnessIndex].data))
    elif tx.type == TransactionType.Upload:
        # add intrinsic cost of calculating the root based on the number of bytecode subsections
        total += contract_state_root_gas_fee(tx.subsectionsNumber)
        # add intrinsic cost of hashing the subsection for verification of the connection with Binary Merkle tree root
        total += sha256_gas_fee(size(tx.witnesses[tx.witnessIndex]))
            
    if tx.type != TransactionType.Mint:
        # add intrinsic cost of calculating the transaction id
        total += sha256_gas_fee(size(tx))
    return total


def intrinsic_gas_fees(tx) -> int:
    """
    Computes intrinsic costs for a transaction
    """
    fees: int = 0
    # add the cost of initializing a vm for the script
    if tx.type == TransactionType.Create or tx.type == TransactionType.Script:
        fees += vm_initialization_gas_fee()
        fees += metadata_gas_fees(tx)
        fees += intrinsic_input_gas_fees(tx)
    return fees


def min_gas(tx) -> int:
    """
    Comutes the minimum amount of gas required for a transaction to begin processing.
    """
    gas = transaction_size_gas_fees(tx) + intrinsic_gas_fees(tx)
    if tx.type == TransactionType.Upload
        # charge additionally for storing bytecode on chain
        gas += transaction_size_gas_fees(size(tx.witnesses[tx.witnessIndex]))
        
    return gas


def max_gas(tx) -> int:
    """
    Computes the amount of gas required to process a transaction.
    """
    gas = min_gas(tx)
    gas = gas + (tx.witnessBytesLimit - tx.witnessBytes) * GAS_PER_BYTE
    if tx.type == TransactionType.Script:
       gas += tx.gasLimit
    return gas
    
    
def maxFee(tx, assetId, gasPrice) -> int:
    """
    Computes the maximum potential amount of fees that may need to be charged to process a transaction.
    """
    maxGas = max_gas(tx)
    feeBalance = gas_to_fee(maxGas, gasPrice)
    # Only base asset can be used to pay for gas
    if assetId == 0:
        return feeBalance
    else:
        return 0


def available_balance(tx, assetId) -> int:
    """
    Make the data message balance available to the script
    """
    availableBalance = sum_inputs(tx, assetId) + sum_data_messages(tx, assetId) + minted(tx, assetId)
    return availableBalance


def unavailable_balance(tx, assetId) -> int:
    sentBalance = sum_outputs(tx, assetId)
    # Total fee balance
    feeBalance = tx.policies.max_fee
    # Only base asset can be used to pay for gas
    if assetId == 0:
        return sentBalance + feeBalance
    return sentBalance


# The sum_data_messages total is not included in the unavailable_balance since it is spendable as long as there 
# is enough base asset amount to cover gas costs without using data messages. Messages containing data can't
# cover gas costs since they are retryable.
return available_balance(tx, assetId) >= (unavailable_balance(tx, assetId) + sum_data_messages(tx, assetId))

Valid Signatures

def address_from(pubkey: bytes) -> bytes:
    return sha256(pubkey)[0:32]

for input in tx.inputs:
    if (input.type == InputType.Coin or input.type == InputType.Message) and input.predicateLength == 0:
        # ECDSA signatures must be 64 bytes
        if tx.witnesses[input.witnessIndex].dataLength != 64:
            return False
        # Signature must be from owner
        if address_from(ecrecover_k1(txhash(), tx.witnesses[input.witnessIndex].data)) != input.owner:
            return False
return True

Signatures and signature verification are specified here.

The transaction hash is computed as defined here.

Predicate Verification

For each input of type InputType.Coin or InputType.Message, and predicateLength > 0, verify its predicate.

Script Execution

Given transaction tx, the following checks must pass:

If tx.scriptLength == 0, there is no script and the transaction defines a simple balance transfer, so no further checks are required.

If tx.scriptLength > 0, the script must be executed. For each asset ID assetId in the input set, the free balance available to be moved around by the script and called contracts is freeBalance[assetId]. The initial message balance available to be moved around by the script and called contracts is messageBalance:

freeBalance[assetId] = available_balance(tx, assetId) - unavailable_balance(tx, assetId)
messageBalance = sum_data_messages(tx, 0)

Once the free balances are computed, the script is executed. After execution, the following is extracted:

  1. The transaction in-memory on VM termination is used as the final transaction which is included in the block.
  2. The unspent free balance unspentBalance for each asset ID.
  3. The unspent gas unspentGas from the $ggas register.

size(tx) encompasses the entire transaction serialized according to the transaction format, including witness data. This ensures every byte of block space either on Fuel or corresponding DA layer can be accounted for.

If the transaction as included in a block does not match this final transaction, the block is invalid.

Fees

The cost of a transaction can be described by:

def cost(tx, gasPrice) -> int:
    return gas_to_fee(min_gas(tx) + tx.gasLimit - unspentGas, gasPrice)

where:

  • min_gas(tx) is the minimum cost of the transaction in gas, including intrinsic gas fees incurred from:
    • The number of bytes comprising the transaction
    • Processing inputs, including predicates
    • Processing outputs
    • VM initialization
  • unspentGas is the amount gas left over after intrinsic fees and execution of the transaction, extracted from the $ggas register. Converting unspent gas to a fee describes how much "change" is left over from the user's payment; the block producer collects this unspent gas as reward.
  • gas_to_fee is a function that converts gas to a concrete fee based on a given gas price.

Fees incurred by transaction processing outside the context of execution are collectively referred to as intrinsic fees. Intrinsic fees include the cost of storing the transaction, calculated on a per-byte basis, the cost of processing inputs and outputs, including predicates and signature verification, and initialization of the VM prior to any predicate or script execution. Because intrinsic fees are independent of execution, they can be determined a priori and represent the bare minimum cost of the transaction.

A naturally occurring result of a variable gas limit is the concept of minimum and maximum fees. The minimum fee is, thus, the exact fee required to pay the fee balance, while the maximum fee is the minimum fee plus the gas limit:

min_gas = min_gas(tx)
max_gas = min_gas + (tx.witnessBytesLimit - tx.witnessBytes) * GAS_PER_BYTE + tx.gasLimit
min_fee = gas_to_fee(min_gas, gasPrice)
max_fee = gas_to_fee(max_gas, gasPrice)

The cost of the transaction cost(tx) must lie within the range defined by [min_fee, max_fee]. min_gas is defined as the sum of all intrinsic costs of the transaction known prior to execution. The definition of max_gas illustrates that the delta between minimum gas and maximum gas is the sum of:

  • The remaining allocation of witness bytes, converted to gas
  • The user-defined tx.gasLimit

Note that gasLimit applies to transactions of type Script. gasLimit is not applicable for transactions of type Create and is defined to equal 0 in the above formula.

A transaction cost cost(tx), in gas, greater than max_gas is invalid and must be rejected; this signifies that the user must provide a higher gas limit for the given transaction. min_fee is the minimum reward the producer is guaranteed to collect, and max_fee is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged intrinsic fees; thus, unspentGas is the remainder of max_gas after intrinsic fees and the variable cost of execution. Calculating a conversion from unspentGas to an unspent fee describes the reward the producer will collect in addition to min_fee.

VM Postcondition Validity Rules

This section defines VM postcondition validity rules for transactions: the requirements for a transaction to be valid after it has been executed.

Given transaction tx, state state, and contract set contracts, the following checks must pass.

Correct Change

If change outputs are present, they must have:

  • if the transaction does not revert;
    • if the asset ID is 0; an amount of unspentBalance + floor((unspentGas * gasPrice) / GAS_PRICE_FACTOR)
    • otherwise; an amount of the unspent free balance for that asset ID after VM execution is complete
  • if the transaction reverts;
    • if the asset ID is 0; an amount of the initial free balance plus (unspentGas * gasPrice) - messageBalance
    • otherwise; an amount of the initial free balance for that asset ID.

State Changes

Transaction processing is completed by removing spent UTXOs from the state and adding created UTXOs to the state.

Coinbase Transaction

The coinbase transaction is a mechanism for block creators to collect transaction fees.

In order for a coinbase transaction to be valid:

  1. It must be a Mint transaction.
  2. The coinbase transaction must be the last transaction within a block, even if there are no other transactions in the block and the fee is zero.
  3. The mintAmount doesn't exceed the total amount of fees processed from all other transactions within the same block.
  4. The mintAssetId matches the assetId that fees are paid in (assetId == 0).

The minted amount of the coinbase transaction intrinsically increases the balance corresponding to the inputContract. This means the balance of mintAssetId is directly increased by mintAmount on the input contract, without requiring any VM execution. Compared to coin outputs, intrinsically increasing a contract balance to collect coinbase amounts prevents the accumulation of dust during low-usage periods.

Cryptographic Primitives

Hashing

All hashing is done with SHA-2-256 (also known as SHA-256), defined in FIPS 180-4.

HashDigest

Output of the hashing function. Exactly 256 bits (32 bytes) long.

Merkle Trees

Two Merkle tree structures are used: a Binary Merkle Tree (to commit to bytecode) and a Sparse Merkle Tree (to commit to contract storage, i.e. state).

Binary Merkle Tree

Binary Merkle trees are constructed in the same fashion as described in Certificate Transparency (RFC-6962), except for using a different hashing function. Leaves are hashed once to get leaf node values and internal node values are the hash of the concatenation of their children (either leaf nodes or other internal nodes).

Nodes contain a single field:

nametypedescription
vHashDigestNode value.

The base case (an empty tree) is defined as the hash of the empty string:

node.v = 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

For leaf node node of leaf data d:

node.v = h(0x00, serialize(d))

For internal node node with children l and r:

node.v = h(0x01, l.v, r.v)

Note that rather than duplicating the last node if there are an odd number of nodes (the Bitcoin design), trees are allowed to be imbalanced. In other words, the height of each leaf may be different. For an example, see Section 2.1.3 of Certificate Transparency (RFC-6962).

Leaves and internal nodes are hashed differently: the one-byte 0x00 is prepended for leaf nodes while 0x01 is prepended for internal nodes. This avoids a second-preimage attack where internal nodes are presented as leaves trees with leaves at different heights.

Binary Merkle Tree Inclusion Proofs

nametypedescription
rootHashDigest[]The expected root of the Merkle tree.
dataBytesThe data of the leaf (unhashed).
siblingsHashDigest[]Sibling hash values, ordered starting from the leaf's neighbor.

A proof for a leaf in a binary Merkle tree, as per Section 2.1.1 of Certificate Transparency (RFC-6962).

In some contexts, the array of sibling hashes is also known as the proof set. Note that proof format prescribes that leaf data be in its original, unhashed state, while the proof set (array of sibling data) uses hashed data. This format precludes the proof set from itself including the leaf data from the leaf undergoing the proof; rather, proof verification explicitly requires hashing the leaf data during the calculation of the proof set root.

Sparse Merkle Tree

Sparse Merkle Trees (SMTs) are sparse, i.e. they contain mostly empty leaves. They can be used as key-value stores for arbitrary data, as each leaf is keyed by its index in the tree. Storage efficiency is achieved through clever use of implicit defaults, avoiding the need to store empty leaves.

Additional rules are added on top of plain binary Merkle trees:

  1. Default values are given to leaf nodes with empty leaves.
  2. While the above rule is sufficient to pre-compute the values of intermediate nodes that are roots of empty subtrees, a further simplification is to extend this default value to all nodes that are roots of empty subtrees. The 32-byte zero, i.e. 0x0000000000000000000000000000000000000000000000000000000000000000, is used as the default value. This rule takes precedence over the above one.
  3. The number of hashing operations can be reduced to be logarithmic in the number of non-empty leaves on average, assuming a uniform distribution of non-empty leaf keys. An internal node that is the root of a subtree that contains exactly one non-empty leaf is replaced by that leaf's leaf node.

Nodes contain a single field:

nametypedescription
vHashDigestNode value.

In the base case, where a sparse Merkle tree has height = 0, the root of a tree is defined as the hash of the empty string:

node.v = 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

When a sparse Merkle tree has a height of 0, it can have no leaves, and, therefore, no default value children. The root is then calculated as the hash of the empty string, similar to that of an empty binary Merkle tree.

For a tree with height > 0, the root of an empty tree is defined as the default value:

node.v = 0x0000000000000000000000000000000000000000000000000000000000000000

Note that this is in contrast to the base case of the sparse and binary Merkle trees, where the root is the hash of the empty string. When a sparse Merkle tree has a height greater than 0, a new tree instance is composed of default value leaves. Nodes containing only default value children have the default value as well. Applying these rules recursively percolates the default value up to the tree's root.

For leaf node node of leaf data d with key k:

node.v = h(0x00, k, h(serialize(d)))

The key of leaf nodes must be prepended, since the index of a leaf node that is not at maximum depth cannot be determined without this information. Leaf values are hashed so that they do not need to be included in full in non-membership proofs.

For internal node node with children l and r:

node.v = h(0x01, l.v, r.v)

Insertion

Before insertion of the key-value pair, each key of the Sparse Merkle Tree should be hashed with sha256 to prevent tree structure manipulations. During the proof verification, the original leaf key should be hashed similarly. Otherwise, the root will not match.

Sparse Merkle Tree Inclusion Proofs

SMTs can further be extended with compact proofs. Merkle proofs are composed, among other things, of a list of sibling node values. We note that, since nodes that are roots of empty subtrees have known values (the default value), these values do not need to be provided explicitly; it is sufficient to simply identify which siblings in the Merkle branch are roots of empty subtrees, which can be done with one bit per sibling.

For a Merkle branch of height h, an h-bit value is appended to the proof. The lowest bit corresponds to the sibling of the leaf node, and each higher bit corresponds to the next parent. A value of 1 indicates that the next value in the list of values provided explicitly in the proof should be used, and a value of 0 indicates that the default value should be used.

A proof into an SMT is structured as:

nametypedescription
depthuint16Depth of the leaf node. The root node is at depth 0. Must be <= 256.
siblingsHashDigest[]Sibling hash values, ordered starting from the leaf's neighbor.
includedSiblingsbyte[32]Bitfield of explicitly included sibling hashes.

The includedSiblings is ordered by most-significant-byte first, with each byte ordered by most-significant-bit first. The lowest bit corresponds to the leaf node level.

A specification describing a suite of test vectors and outputs of a Sparse Merkle Tree is here.

ECDSA Public-Key Cryptography

Consensus-critical data is authenticated using ECDSA, with the curve secp256k1. A highly-optimized library is available in C, with wrappers in Go and Rust.

Public keys are encoded in uncompressed form, as the concatenation of the x and y values. No prefix is needed to distinguish between encoding schemes as this is the only encoding supported.

Deterministic signatures (RFC-6979) should be used when signing, but this is not enforced at the protocol level as it cannot be.

Signatures are represented as the r and s (each 32 bytes), and v (1-bit) values of the signature. r and s take on their usual meaning (see: SEC 1, 4.1.3 Signing Operation), while v is used for recovering the public key from a signature more quickly (see: SEC 1, 4.1.6 Public Key Recovery Operation). Only low-s values in signatures are valid (i.e. s <= secp256k1.n//2); s can be replaced with -s mod secp256k1.n during the signing process if it is high. Given this, the first bit of s will always be 0, and can be used to store the 1-bit v value.

v represents the parity of the Y component of the point, 0 for even and 1 for odd. The X component of the point is assumed to always be low, since the possibility of it being high is negligible.

Putting it all together, the encoding for signatures is:

|    32 bytes   ||           32 bytes           |
[256-bit r value][1-bit v value][255-bit s value]

This encoding scheme is derived from EIP 2098: Compact Signature Representation.

EdDSA Public-Key Cryptography

Ed25519 is supported for use by applications built on Fuel. Edwards curve operations are performed by the ed25519-dalek Rust library.

Public keys are encoded in compressed form as specified by the Ed25519 format RFC-8032 5.1.5. Point compression is performed by replacing the most significant bit in the final octet of the y coordinate with the sign bit from the x coordinate:

#![allow(unused)]
fn main() {
let mut pk = y;
pk ^= x.is_negative().unwrap_u8() << 7;
}

Public keys are required to be strong enough to prevent malleability, and are checked for weakness during signature verification.

Signatures are 64 bytes, represented as the concatenation of R (32 bytes) and S (32 bytes) Where R and S are defined in RFC-8032 5.1.6.

Signatures must conform to strict verification requirements to avoid malleability concerns. While this is not part of the original Ed25519 specification, it has become a growing concern especially in cryptocurrency applications.

JSON Format for Contract Storage Initializers

Contracts can request that certain storage slots are initialized to specific values. These initialized slots are represented in JSON format as an array where each element represents a storage slot and has the following properties:

  • "key": String, a 32-byte key for a given storage slot;
  • "value": String, a 32-byte value that initializes the slot;

For instance, the following is a JSON object that requests that the 3 storage slots with keys 0x11..11, 0x22..22, and 0x33..33, are respectively initialized to the values indicated.

[
  {
    "key": "0x1111111111111111111111111111111111111111111111111111111111111111",
    "value": "0x1010101010101010101010101010101010101010101010101010101010101010"
  }, 
  {
    "key": "0x2222222222222222222222222222222222222222222222222222222222222222",
    "value": "0x2020202020202020202020202020202020202020202020202020202020202020"
  },
  {
    "key": "0x3333333333333333333333333333333333333333333333333333333333333333",
    "value": "0x0303030303030303030303030303030303030303030303030303030303030303"
  },
]

Block Header

Application Header

The application header is a network-agnostic block header. Different networks may wrap the application header in a consensus header, depending on their consensus protocol.

nametypedescription
da_heightuint64Height of the data availability layer up to which (inclusive) input messages are processed.
consensusParametersVersionuint32The version of the consensus parameters used to execute this block.
stateTransitionBytecodeVersionuint32The version of the state transition bytecode used to execute this block.
txCountuint16Number of transactions in this block.
message_receipt_countuint32Number of output messages in this block.
txRootbyte[32]Merkle root of transactions in this block.
message_outbox_rootbyte[32]Merkle root of output messages messageId in this block.
event_inbox_rootbyte[32]Merkle root of all events imported from L1 in this block. The order of the events added to the Merkle tree is the L1 block order, and the index of each event within each block

Layer 1 Relayer/Bridge Protocol

The Fuel relayer/bridge protocol is a set of rules that govern the interaction between the Fuel blockchain and the Layer 1 (L1) blockchain (e.g. Ethereum).

The Fuel blockchain can emit messages that will be processed by the smart contract on the L1 blockchain. The smart contract on the L1 can also emit events that will be processed by the Fuel blockchain. This is used to move any data between the L1 blockchain and the Fuel blockchain.

Fuel Message Outbox

The message outbox is the set of messages sent to the L1 blockchain from the Fuel blockchain.

Fuel Event Inbox

The event inbox is the set of events received from the L1 blockchain by the Fuel blockchain.

The block producer will receive a list of events from the L1 by some relayer, and then include the merkle root of the events in the block header.

There are two types of events that can be received from the L1:

  1. Messages
  2. Transactions

Messages

An arbitrary message sent from the L1 to the Fuel blockchain. This can be used to move assets from the L1 to the Fuel blockchain or send other arbitrary information to the Fuel blockchain.

nametypedescription
senderbytes[32]The identity of the sender of the message on the L1
recipientbytes[32]The recipient of the message on the Fuel Blockchain
noncebytes[32]Unique identifier of the message assigned by the L1 contract
amountuint64The amount of the base asset transfer
databyte[]Arbitrary message data

Transactions

These are transactions that are submitted on the L1 that must be executed on the Fuel blockchain. This "Forced Transaction Inclusion" is a security feature that allows participants of the Fuel Blockchain to access their funds in the (unlikely) event that the Fuel blockchain block production is compromised or malicious, e.g. the block producer is censoring transactions.

nametypedescription
noncebytes[32]Unique identifier of the transaction assigned by the L1 contract
max_gasuint64The maximum amount of gas allowed to use on Fuel Blockchain
serialized_transactionbyte[]The serialized transaction bytes following canonical serialization

The serialized_transaction can be any transaction variant except the Mint transaction, which is only ever created by the block producer. Mint transactions will be rejected by the Fuel blockchain if they are relayed from the L1.

Ordering

It is important that the L1 events are ordered correctly when they are relayed to the Fuel blockchain. The events will be ordered by the L1 block height and then by the index of the event within the block.

The order is important because a merkle root will be generated each time events from L1 are included in a Fuel block. This merkle root can later be used to prove that an arbitrary event was included on that block without having to store every event on the block header explicitly. Just the merkle root will be on the block header. The order of the events affects the value of the merkle root.

Application Binary Interface (ABI)

This document describes and specifies the ABI (Application Binary Interface) of the FuelVM, the Sway programming language, and contracts written in Sway.

JSON ABI Format

The JSON of an ABI is the human-readable representation of the interface of a Sway contract.

Spec Version

Current specVersion is 1.0

The version above should be updated each time this spec changes and the JSON ABI generator should be updated with the new changes along with an increment in the spec version.

Notation

Before describing the format of the JSON ABI, we provide some definitions that will make the JSON ABI spec easier to read.

Given the example below:

#![allow(unused)]
fn main() {
struct Foo { x: bool }
struct Bar<T> { y: T }

fn baz(input1: Foo, input2: Bar<u64>); // an ABI function
}

we define the following expressions:

  • type concrete declaration: the declaration or definition of a type which can be generic. struct Foo { .. } and struct Bar<T> { .. } in the example above are both type declarations. Note that generic types may have multiple type concrete declaration.
  • type metadata declaration: the declaration or definition of a type which can be generic. struct Foo { .. } and struct Bar<T> { .. } in the example above are both type declarations. The metadata declaration contains component details about the type. And a single type metadata declaration is generated per type, even for generic types.
  • type application: the application or use of a type. Foo and Bar<u64> in fn baz(input1: Foo, input2: Bar<u64>); in the example above are both applications of the type declarations struct Foo { .. } and struct Bar<T> { .. } respectively.
  • type parameter: a generic parameter used in a type declaration. T in struct Bar<T> in the example above is a type parameter.
  • type argument: an application of a type parameter used in a type application. u64 in input2: Bar<u64> in the example above is a type argument.

JSON ABI Spec

The ABI of a contract is represented as a JSON object containing the following properties:

  • "specVersion": a string representing the version pointing to this document versioning. specVersion enables the reader of the JSON ABI to find the correct specification for that file, this can be done by comparing it to value in spec version.
  • "encodingVersion": a string representing the version of the ABIEncode and ABIDecode used in this program.
  • "programType": a string that can be "script", "contract", "predicate", "library". This is used by the SDK to generate types without having to manually specify the program type.
  • "concreteTypes": an array describing all the type concrete declarations used (or transitively used) in the ABI. Each type concrete declaration is a JSON object that contains the following properties:
    • "type": a string representing the type, the sha256 of this string generates the concreteTypeId.
    • "concreteTypeId": a unique string hash based ID. Generated as specified in Hash Based Ids.
    • "metadataTypeId": the type metadata declaration ID of this type, if the type metadata has components or is generic, otherwise nonexistent.
    • "typeArguments": an array of type concrete declarations hash based IDs of the type parameters of the type, if the type is generic, otherwise nonexistent.
  • "metadataTypes": an array describing all the type metadata declarations used (or transitively used) in the ABI. Each type metadata declaration is a JSON object that contains the following properties:
    • "type": a string representation of the type metadata declaration. The section JSON ABI Format for Each Possible Metadata Type Declaration specifies the format for each possible type.
    • "metadataTypeId": a unique integer ID.
    • "components": an array of the components of a given type, if any, otherwise nonexistent. Each component is a type application represented as a JSON object that contains the following properties:
      • "name": the name of the component.
      • "typeId": the type metadata declaration ID (number) or type concrete declaration hash based ID (string) of the type of the component.
      • "typeArguments": an array of the type arguments used when applying the type of the component, if the type is generic, otherwise nonexistent. Each type argument is a type application represented as a JSON object that contains the following properties:
        • "typeId": the type metadata declaration ID (number) or type concrete declaration hash based ID (string) of the type of the component.
        • "typeArguments": an array of the type arguments used when applying the type of the type argument, if the type is generic, otherwise nonexistent. The format of the elements of this array recursively follows the rules described in this section.
    • "typeParameters": an array of type metadata declaration ID of the type parameters of the type, if the type is generic, otherwise nonexistent. Each type parameter is a type declaration and is represented as described in Generic Type Parameter.
  • "functions": an array describing all the functions in the ABI. Each function is a JSON object that contains the following properties:
    • "name": the name of the function
    • "inputs": an array of objects that represents the inputs to the function (i.e. its parameters). Each input is a type application represented as a JSON object that contains the following properties:
      • "name": the name of the input.
      • "concreteTypeId": the type concrete declaration hash based ID of the type of the input.
    • "output": the type concrete declaration hash based ID of the type being returned by the function.
    • "attributes": an optional array of attributes. Each attribute is explained in the dedicated section and is represented as a JSON object that contains the following properties:
      • "name": the name of the attribute.
      • "arguments": an array of attribute arguments.
  • "loggedTypes": an array describing all instances of log or logd in the contract's bytecode. Each instance is a JSON object that contains the following properties:
    • "logId": a string containing the 64bit hash based decimal ID calculated from the first 8 bytes of the sha256 of a string that represents the type logged as defined in Hash Based Ids. The log and logd instructions must set their $rB register to that ID.
    • "concreteTypeId": the type concrete declaration hash based ID of the value being logged.
  • "messagesTypes": an array describing all instances of smo in the contract's bytecode. Each instance is a JSON object that contains the following properties:
    • "message_id": a unique string ID.
    • "concreteTypeId": the type concrete declaration hash based ID of the message data being sent.
  • "configurables": an array describing all configurable variables used in the contract. Each configurable variable is represented as a JSON object that contains the following properties:
    • "name": the name of the configurable variable.
    • "concreteTypeId": the type concrete declaration hash based ID of the type of the configurable variable.
    • "offset": the specific offset within the contract's bytecode, in bytes, to the data section entry for the configurable variable.

Note: This JSON should be both human-readable and parsable by the tooling around the FuelVM and the Sway programming language. There is a detailed specification for the binary encoding backing this readable descriptor. The Function Selector Encoding section specifies the encoding for the function being selected to be executed and each of the argument types.

Attributes Semantics

Attribute nameAttribute argumentsSemantics
storageread and/or writeSpecifies if a function reads or writes to/from storage
payableNoneSpecifies if a function can accept coins: a function without payable attribute must not accept coins
testNoneSpecifies if a function is a unit test
inlinenever or always, but not bothSpecifies if a function should be inlined during code generation
doc-commentStringDocumentation comment
docNot defined yetNot defined yet

A Simple Example

Below is a simple example showing how the JSON ABI for an example that does not use generic or complex types. We will later go over more complex examples.

#![allow(unused)]
fn main() {
abi MyContract {
    fn first_function(arg: u64) -> bool;
    fn second_function(arg: b256);
}
}

the JSON representation of this ABI looks like:

{
  "concreteTypes": [
    {
      "type": "u64",
      "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
    },
    {
      "type": "b256",
      "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b"
    },
    {
      "type": "bool",
      "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903"
    },
    {
      "type": "()",
      "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d"
    }
  ],
  "metadataTypes": [],
  "functions": [
    {
      "inputs": [
        {
          "name": "arg",
          "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
        }
      ],
      "name": "first_function",
      "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903"
    },
    {
      "inputs": [
        {
          "name": "arg",
          "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b"
        }
      ],
      "name": "second_function",
      "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d"
    }
  ],
  "loggedTypes": []
}

JSON ABI Format for Each Possible Metadata Type Declaration

Below is a list of the JSON ABI formats for each possible metadata type declaration:

struct

{
  "metadataTypeId": <id>,
  "type": "struct <struct_name>",
  "components": [
    {
      "name": "<field1_name>",
      "typeId": "<field1_type_id>",
      "typeArguments": [
        {
          "typeId": "<type_arg1_type_id>",
          "typeArguments": ...
        },
        {
          "typeId": "<type_arg2_type_id>",
          "typeArguments": ...
        },
        ...
      ]
    },
    {
      "name": "<field2_name>",
      "typeId": "<field2_type_id>",
      "typeArguments": [
        {
          "typeId": "<type_arg1_type_id>",
          "typeArguments": ...
        },
        {
          "typeId": "<type_arg2_type_id>",
          "typeArguments": ...
        },
        ...
      ]
    },
    ...
  ],
  "typeParameters": [
    <type_param1_type_id>,
    <type_param2_type_id>,
    ...
  ]
}

enum

{
  "metadataTypeId": <id>,
  "type": "enum <enum_name>",
  "components": [
    {
      "name": "<variant1_name>",
      "typeId": "<variant1_type_id>",
      "typeArguments": [
        {
          "typeId": "<type_arg1_type_id>",
          "typeArguments": ...
        },
        {
          "typeId": "<type_arg2_type_id>",
          "typeArguments": ...
        },
        ...
      ]
    },
    {
      "name": "<variant2_name>",
      "typeId": "<variant2_type_id>",
      "typeArguments": [
        {
          "typeId": "<type_arg1_type_id>",
          "typeArguments": ...
        },
        {
          "typeId": "<type_arg2_type_id>",
          "typeArguments": ...
        },
        ...
      ]
    },
    ...
  ],
  "typeParameters": [
    <type_param1_type_id>,
    <type_param2_type_id>,
    ...
  ]
}

array

{
  "metadataTypeId": <id>,
  "type": "[_; <n>]",
  "components": [
    {
      "name": "__array_element",
      "typeId": "<element_type>",
      "typeArguments": ...
    }
    {
      "name": "__array_element",
      "typeId": "<element_type_id>",
      "typeArguments": [
        {
          "typeId": "<type_arg1_type_id>",
          "typeArguments": ...
        },
        {
          "typeId": "<type_arg2_type_id>",
          "typeArguments": ...
        },
        ...
      ]
    },
  ]
}
  • <n> is the size of the array.

tuple

{
  "metadataTypeId": <id>,
  "type": "(_, _, ...)",
  "components": [
    {
      "name": "__tuple_element",
      "typeId": "<field1_type_id>",
      "typeArguments": [
        {
          "typeId": "<type_arg1_type_id>",
          "typeArguments": ...
        },
        {
          "typeId": "<type_arg2_type_id>",
          "typeArguments": ...
        },
        ...
      ]
    },
    {
      "name": "__tuple_element",
      "typeId": "<field2_type_id>",
      "typeArguments": [
        {
          "typeId": "<type_arg1_type_id>",
          "typeArguments": ...
        },
        {
          "typeId": "<type_arg2_type_id>",
          "typeArguments": ...
        },
        ...
      ]
    },
    ...
  ]
}

Generic Type Parameter

{
  "metadataTypeId": <id>,
  "type": "generic <name>"
}

<name> is the name of the generic parameter as specified in the struct or enum declaration that uses it.

Some Complex Examples

An Example with Non-Generic Custom Types

Given the following ABI declaration:

#![allow(unused)]
fn main() {
enum MyEnum {
    Foo: u64,
    Bar: bool,
}

struct MyStruct {
    bim: u64,
    bam: MyEnum,
}

abi MyContract {
    /// this is a doc comment
    #[payable, storage(read, write)]
    fn complex_function(
        arg1: ([str[5]; 3], bool, b256),
        arg2: MyStruct,
    );
}
}

its JSON representation would look like:

{
  "concreteTypes": [
    {
      "type": "([str[5]; 3], bool, b256)",
      "concreteTypeId": "625531542be70834dd127e771101ac1014111718451bfae996d97abe700c66a5",
      "metadataTypeId": 1,
    },
    {
      "type": "str[5]",
      "concreteTypeId": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7",
    },
    {
      "type": "struct MyStruct",
      "concreteTypeId": "392d58c694d2d91f3025f2bccfadacf2a105936f5da881b0899185d49f264522",
      "metadataTypeId": 4,
    },
    {
      "type": "u64",
      "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
    },
    {
      "type": "b256",
      "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b"
    },
    {
      "type": "bool",
      "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903"
    },
    {
      "type": "()",
      "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d"
    }
  ],
  "metadataTypes": [
    {
      "metadataTypeId": 1,
      "type": "(_, _, _)",
      "components": [
        {
          "name": "__tuple_element",
          "typeId": 2,
        },
        {
          "name": "__tuple_element",
          "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903",
        },
        {
          "name": "__tuple_element",
          "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b",
        }
      ]
    },
    {
      "metadataTypeId": 2,
      "type": "[_; 3]",
      "components": [
        {
          "name": "__array_element",
          "typeId": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7",
        }
      ]
    },
    {
      "metadataTypeId": 3,
      "type": "enum MyEnum",
      "components": [
        {
          "name": "Foo",
          "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0",
        },
        {
          "name": "Bar",
          "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903",
        }
      ]
    },
    {
      "metadataTypeId": 4,
      "type": "struct MyStruct",
      "components": [
        {
          "name": "bim",
          "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0",
        },
        {
          "name": "bam",
          "typeId": 3,
        }
      ]
    },
  ],
  "functions": [
    {
      "inputs": [
        {
          "name": "arg1",
          "concreteTypeId": "625531542be70834dd127e771101ac1014111718451bfae996d97abe700c66a5",
        },
        {
          "name": "arg2",
          "concreteTypeId": "392d58c694d2d91f3025f2bccfadacf2a105936f5da881b0899185d49f264522"
        }
      ],
      "name": "complex_function",
      "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d",
      "attributes": [
        {
          "name": "doc-comment",
          "arguments": [" this is a doc comment"]
        },
        {
          "name": "payable",
        },
        {
          "name": "storage",
          "arguments": ["read", "write"]
        }
      ]
    }
  ],
  "loggedTypes": []
}

An Example with Generic Types

Given the following ABI declaration:

#![allow(unused)]
fn main() {
enum MyEnum<T, U> {
    Foo: T,
    Bar: U,
}
struct MyStruct<W> {
    bam: MyEnum<W, W>,
}

abi MyContract {
    fn complex_function(
        arg1: MyStruct<b256>,
    );
}
}

its JSON representation would look like:

{
  "concreteTypes": [
    {
      "type": "struct MyStruct<b256>",
      "concreteTypeId": "3ddd5c1768dd7869663dc2f868ea8a8ce68bd6064244dbc4286e2c921c8ce962",
      "metadataTypeId": 5,
      "typeArguments": [
        "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b"
      ]
    },
    {
      "type": "b256",
      "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b",
    },
    {
      "type": "()",
      "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d",
    }
  ],
  "metadataTypes": [
    {
      "metadataTypeId": 1,
      "type": "enum MyEnum",
      "components": [
        {
          "name": "Foo",
          "typeId": 2,
        },
        {
          "name": "Bar",
          "typeId": 3,
        }
      ],
      "typeParameters": [2, 3]
    },
    {
      "metadataTypeId": 2,
      "type": "generic T",
    },
    {
      "metadataTypeId": 3,
      "type": "generic U",
    },
    {
      "metadataTypeId": 4,
      "type": "generic W",
    },
    {
      "metadataTypeId": 5,
      "type": "struct MyStruct",
      "components": [
        {
          "name": "bam",
          "typeId": 1,
          "typeArguments": [
            {
              "typeId": 4,
            },
            {
              "typeId": 4,
            }
          ]
        }
      ],
      "typeParameters": [4]
    }
  ],
  "functions": [
    {
      "inputs": [
        {
          "name": "arg1",
          "concreteTypeId": "3ddd5c1768dd7869663dc2f868ea8a8ce68bd6064244dbc4286e2c921c8ce962"
        }
      ],
      "name": "complex_function",
      "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d"
    }
  ],
  "loggedTypes": []
}

An Example with Logs

Given the following contract:

#![allow(unused)]
fn main() {
struct MyStruct<W> {
    x: W,
}

abi MyContract {
    fn logging();
}

...

fn logging() {
    log(MyStruct { x: 42 });
    log(MyStruct { x: true });
}
}

its JSON representation would look like:

{
  "concreteTypes": [
    {      
      "type": "struct MyStruct<bool>",
      "concreteTypeId": "eca2a040ce95fc19b7cd5f75bac530d052484d0b1a49267a2eb07a7a1b00c389",
      "metadataTypeId": 1,
      "typeArguments": [
        "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903"
      ]
    },
    {      
      "type": "struct MyStruct<u64>",
      "concreteTypeId": "b2fa346d9ca66ceca61951a27dba2977b2a82b8aa8600670604f286a1393dffe",
      "metadataTypeId": 1,
      "typeArguments": [
        "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0"
      ]
    },
    {      
      "type": "bool",
      "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903",
    },
    {      
      "type": "u64",
      "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0",
    },
    {
      "type": "()",
      "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d",
    }
  ],
  "metadataTypes": [
    {
      "metadataTypeId": 1,
      "type": "struct MyStruct",
      "components": [
        {
          "name": "x",
          "typeId": 2,
          "typeArguments": null
        }
      ],
      "typeParameters": [2]
    },
    {
      "metadataTypeId": 2,
      "type": "generic W",
    },
  ],
  "functions": [
    {
      "inputs": [],
      "name": "logging",
      "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d"
    }
  ],
  "loggedTypes": [
    {
      "logId": "12896678128313068780",
      "concreteTypeId": "b2fa346d9ca66ceca61951a27dba2977b2a82b8aa8600670604f286a1393dffe"
    },
    {
      "logId": "16383228984366451899",
      "concreteTypeId": "eca2a040ce95fc19b7cd5f75bac530d052484d0b1a49267a2eb07a7a1b00c389"
    }
  ]
}

The logIds are calculated from:

  • First 8 bytes of sha256("struct MyStruct<u64>") => "12896678128313068780"
  • First 8 bytes of sha256("struct MyStruct<bool>") => "16383228984366451899"

Receipts

Upon execution of ABI calls, i.e scripts being executed, a JSON object representing a list of receipts will be returned to the caller. Below is the JSON specification of the possible receipt types. The root will be receipts_root which will include an array of receipts.

{
  "receipts_list":[
    {
      "type":"<receipt_type>",
      ...
    },
    ...
  ]
}

All receipts will have a type property:

Then, each receipt type will have its own properties. Some of these properties are related to the FuelVM's registers, as specified in more detail here.

Important note: For the JSON representation of receipts, we represent 64-bit unsigned integers as a JSON String due to limitations around the type Number in the JSON specification, which only supports numbers up to 2^{53-1}, while the FuelVM's registers hold values up to 2^64.

Panic Receipt

  • type: Panic.
  • id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context. null otherwise.
  • reason: Optional decimal string representation of an 8-bit unsigned integer; panic reason. Not included in canonical receipt form.
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
  • contractId: Optional hexadecimal string representation of the 256-bit (32-byte) contract ID if applicable. null otherwise. Not included in canonical receipt form. Primary use is for access-list estimation by SDK.
{
  "type": "Panic",
  "id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "reason": "1",
  "pc": "0xffffffffffffffff",
  "is": "0xfffffffffffffffe",
  "contractId": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

Return Receipt

  • type: Return.
  • id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context; null otherwise.
  • val: Decimal string representation of a 64-bit unsigned integer; value of register $rA.
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
{
  "type": "Return",
  "id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "val": "18446744073709551613",
  "pc": "0xffffffffffffffff",
  "is": "0xfffffffffffffffe"
}

Call Receipt

  • type: Call.
  • id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context; null otherwise.
  • to: Hexadecimal representation of the 256-bit (32-byte) contract ID of the callee.
  • amount: Decimal string representation of a 64-bit unsigned integer; amount of coins to forward.
  • asset_id: Hexadecimal string representation of the 256-bit (32-byte) asset ID of coins to forward.
  • gas: Decimal string representation of a 64-bit unsigned integer; amount gas to forward; value in register $rD.
  • param1: Hexadecimal string representation of a 64-bit unsigned integer; first parameter, holds the function selector.
  • param2: Hexadecimal string representation of a 64-bit unsigned integer; second parameter, typically used for the user-specified input to the ABI function being selected.
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
{
  "type": "Call",
  "id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "to": "0x1c98ff5d121a6d5afc8135821acb3983e460ef0590919266d620bfc7b9b6f24d",
  "amount": "10000",
  "asset_id": "0xa5149ac6064222922eaa226526b0d853e7871e28c368f6afbcfd60a6ef8d6e61",
  "gas": "500",
  "param1": "0x28f5c28f5c28f5c",
  "param2": "0x68db8bac710cb",
  "pc": "0xffffffffffffffff",
  "is": "0xfffffffffffffffe"
}

Log Receipt

  • type: Log.
  • id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context. null otherwise.
  • ra: Decimal string representation of a 64-bit unsigned integer; value of register $rA.
  • rb: Decimal string representation of a 64-bit unsigned integer; value of register $rB.
  • rc: Decimal string representation of a 64-bit unsigned integer; value of register $rC.
  • rd: Decimal string representation of a 64-bit unsigned integer; value of register $rD.
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
{
  "type": "Log",
  "id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "ra": "1844674407370",
  "rb": "1844674407371",
  "rc": "1844674407372",
  "rd": "1844674407373",
  "pc": "0xffffffffffffffff",
  "is": "0xfffffffffffffffe"
}

Mint Receipt

  • type: Mint.
  • sub_id: Hexadecimal string representation of the 256-bit (32-byte) asset sub identifier ID; derived from register $rB.
  • contract_id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context.
  • val: Decimal string representation of a 64-bit unsigned integer; value of register $rA.
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
{
  "type": "Mint",
  "sub_id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "contract_id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "val": "18446744073709551613",
  "pc": "0xffffffffffffffff",
  "is": "0xfffffffffffffffe"
}

Burn Receipt

  • type: Burn.
  • sub_id: Hexadecimal string representation of the 256-bit (32-byte) asset sub identifier ID; derived from register $rB.
  • contract_id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context.
  • val: Decimal string representation of a 64-bit unsigned integer; value of register $rA.
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
{
  "type": "Burn",
  "sub_id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "contract_id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "val": "18446744073709551613",
  "pc": "0xffffffffffffffff",
  "is": "0xfffffffffffffffe"
}

LogData Receipt

  • type: LogData.
  • id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context. null otherwise.
  • ra: Decimal string representation of a 64-bit unsigned integer; value of register $rA
  • rb: Decimal string representation of a 64-bit unsigned integer; value of register $rB
  • ptr: Hexadecimal string representation of a 64-bit unsigned integer; value of register $rC.
  • len: Decimal string representation of a 64-bit unsigned integer; value of register $rD.
  • digest: Hexadecimal string representation of the 256-bit (32-byte) hash of MEM[$rC, $rD].
  • data: Hexadecimal string representation of the value of the memory range MEM[$rC, $rD].
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
{
  "type": "LogData",
  "id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "ra": "1844674407370",
  "rb": "1844674407371",
  "ptr": "0x1ad7f29abcc",
  "len": "66544",
  "digest": "0xd28b78894e493c98a196aa51b432b674e4813253257ed9331054ee8d6813b3aa",
  "pc": "0xffffffffffffffff",
  "data": "0xa7c5ac471b47",
  "is": "0xfffffffffffffffe"
}

ReturnData Receipt

  • type: ReturnData.
  • id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context. null otherwise.
  • ptr: Hexadecimal string representation of a 64-bit unsigned integer; value of register $rA.
  • len: Decimal string representation of a 64-bit unsigned integer; value of register $rB.
  • digest: Hexadecimal string representation of 256-bit (32-byte), hash of MEM[$rA, $rB].
  • data: Hexadecimal string representation of the value of the memory range MEM[$rA, $rB].
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
{
  "type": "ReturnData",
  "id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "ptr": "0x1ad7f29abcc",
  "len": "1844",
  "digest": "0xd28b78894e493c98a196aa51b432b674e4813253257ed9331054ee8d6813b3aa",
  "pc": "0xffffffffffffffff",
  "data": "0xa7c5ac471b47",
  "is": "0xfffffffffffffffe"
}

Revert Receipt

  • type: Revert.
  • id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context. null otherwise.
  • val: Decimal string representation of a 64-bit unsigned integer; value of register $rA.
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
{
  "type": "Revert",
  "id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "val": "1844674407372",
  "pc": "0xffffffffffffffff",
  "is": "0xfffffffffffffffe"
}

Transfer Receipt

  • type: Transfer.
  • id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context. null otherwise.
  • to: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the recipient contract.
  • amount: Decimal string representation of a 64-bit unsigned integer; amount of coins to forward.
  • asset_id: Hexadecimal string representation of the 256-bit (32-byte) asset ID of coins to forward.
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
{
  "type": "Transfer",
  "id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "to": "0x1c98ff5d121a6d5afc8135821acb3983e460ef0590919266d620bfc7b9b6f24d",
  "amount": "10000",
  "asset_id": "0xa5149ac6064222922eaa226526b0d853e7871e28c368f6afbcfd60a6ef8d6e61",
  "pc": "0xffffffffffffffff",
  "is": "0xfffffffffffffffe"
}

TransferOut Receipt

  • type: TransferOut.
  • id: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context. null otherwise.
  • to: Hexadecimal string representation of the 256-bit (32-byte) address to transfer coins to.
  • amount: Decimal string representation of a 64-bit unsigned integer; amount of coins to forward.
  • asset_id: Hexadecimal string representation of the 256-bit (32-byte) asset ID of coins to forward.
  • pc: Hexadecimal string representation of a 64-bit unsigned integer; value of register $pc.
  • is: Hexadecimal string representation of a 64-bit unsigned integer; value of register $is.
{
  "type": "TransferOut",
  "id": "0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "to": "0x1c98ff5d121a6d5afc8135821acb3983e460ef0590919266d620bfc7b9b6f24d",
  "amount": "10000",
  "asset_id": "0xa5149ac6064222922eaa226526b0d853e7871e28c368f6afbcfd60a6ef8d6e61",
  "pc": "0xffffffffffffffff",
  "is": "0xfffffffffffffffe"
}

ScriptResult Receipt

  • type: ScriptResult.
  • result: Hexadecimal string representation of a 64-bit unsigned integer; 0 if script exited successfully, any otherwise.
  • gas_used: Decimal string representation of a 64-bit unsigned integer; amount of gas consumed by the script.
{
  "type": "ScriptResult",
  "result": "0x00",
  "gas_used": "400"
}

MessageOut Receipt

  • type: MessageOut.
  • sender: Hexadecimal string representation of the 256-bit (32-byte) address of the message sender: MEM[$fp, 32].
  • recipient: Hexadecimal string representation of the 256-bit (32-byte) address of the message recipient: MEM[$rA, 32].
  • amount: Hexadecimal string representation of a 64-bit unsigned integer; value of register $rD.
  • nonce: Hexadecimal string representation of the 256-bit (32-byte) message nonce as described here.
  • len: Decimal string representation of a 16-bit unsigned integer; value of register $rC.
  • digest: Hexadecimal string representation of 256-bit (32-byte), hash of MEM[$rB, $rC].
  • data: Hexadecimal string representation of the value of the memory range MEM[$rB, $rC].
{
  "type": "MessageOut",
  "sender": "0x38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff05139150017c9e",
  "recipient": "0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
  "amount": "0xe6d8fe4710162c2e",
  "nonce": "0x47101017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
  "len": "65535",
  "digest": "0xd28b78894e493c98a196aa51b432b674e4813253257ed9331054ee8d6813b3aa",
  "data": "0xa7c5ac471b47"
}

Function Selector Encoding

To select which function you want to call, first, this function must be in an ABI struct of a Sway program. For instance:

#![allow(unused)]
fn main() {
abi MyContract {
    fn foo(a: u64);
    fn bar(a: InputStruct );
} {
    fn baz(a: ()) { }
}
}

The function selector is the first 4 bytes of the SHA-256 hash function of the signature of the Sway function being called. Then, these 4 bytes are right-aligned to 8 bytes, left-padded with zeroes.

Note: The word size for the FuelVM is 8 bytes.

Function Signature

The signature is composed of the function name with the parenthesized list of comma-separated parameter types without spaces. All strings encoded with UTF-8. For custom types such as enum and struct, there is a prefix added to the parenthesized list (see below). Generic struct and enum types also accept a list of comma-separated type arguments in between angle brackets right after the prefix.

For instance, to compute the selector for the following function:

#![allow(unused)]
fn main() {
    fn entry_one(arg: u64);
}

we should pass "entry_one(u64)" to the sha256() hashing algorithm. The full digest would be:

0x0c36cb9cb766ff60422db243c4fff06d342949da3c64a3c6ac564941f84b6f06

Then we would get only the first 4 bytes of this digest and left-pad it to 8 bytes:

0x000000000c36cb9c

The table below summarizes how each function argument type is encoded

TypeEncoding
boolbool
u8u8
u16u16
u32u32
u64u64
b256b256
structs<<arg1>,<arg2>,...>(<ty1>,<ty2>,...) where <ty1>, <ty2>, ... are the encoded types of the struct fields and <arg1>, <arg2>, ... are the encoded type arguments
enume<<arg1>>,<arg_2>,...>(<ty1>,<ty2>,...) where <ty1>, <ty2>, ... are the encoded types of the enum variants and <arg1>, <arg2>, ... are the encoded type arguments
str[<n>]str[<n>]
arraya[<ty>;<n>] where <ty> is the encoded element type of the array and <n> is its length
tuple(<ty1>,<ty2>,...) where <ty1>, <ty2>, ... are the encoded types of the tuple fields

Note: Non-generic structs and enums do not require angle brackets.

A Complex Example

#![allow(unused)]
fn main() {
enum MyEnum<V> {
    Foo: u64,
    Bar: bool,
}
struct MyStruct<T, U> {
    bim: T,
    bam: MyEnum<u64>,
}

struct MyOtherStruct {
    bom: u64,
}

fn complex_function(
    arg1: MyStruct<[b256; 3], u8>,
    arg2: [MyStruct<u64, bool>; 4],
    arg3: (str[5], bool),
    arg4: MyOtherStruct,
);
}

is encoded as:

abi MyContract {
    complex_function(s<a[b256;3],u8>(a[b256;3],e<u64>(u64,bool)),a[s<u64,bool>(u64,e<u64>(u64,bool));4],(str[5],bool),s(u64))
}

which is then hashed into:

51fdfdadc37ff569e281a622281af7ec055f8098c40bc566118cbb48ca5fd28b

and then the encoded function selector is:

0x0000000051fdfdad

Argument Encoding

Version 0

:warning: This version is being deprecated for Version 1 (see below).

When crafting transaction script data, you must encode the arguments you wish to pass to the script.

Types

These are the available types that can be encoded in the ABI:

  • Unsigned integers:
    • u8, 8 bits.
    • u16, 16 bits.
    • u32, 32 bits.
    • u64, 64 bits.
    • u128, 128 bits.
    • u256, 256 bits.
  • Boolean: bool, either 0 or 1 encoded identically to u8.
  • B256: b256, arbitrary 256-bits value.
  • Address : address, a 256-bit (32-byte) address.
  • Fixed size string
  • Array
  • Enums (sum types)
  • Structs
  • Vectors
  • Tuples

These types are encoded in-place. Here's how to encode them. We define enc(X) the encoding of the type X.

Unsigned Integers

u<M> where M is either 8, 16, 32, 64, 128 or 256 bits.

enc(X) is the big-endian (i.e. right-aligned) representation of X left-padded with zero-bytes.

  • In the case of M being 8, 16, 32 or 64, total length must be 8 bytes.
  • If M is 128, total length must be 16 bytes.
  • If M is 256, total length must be 32 bytes.

Note: since all integer values are unsigned, there is no need to preserve the sign when extending/padding; padding with only zeroes is sufficient._

Example:

Encoding 42 yields: 0x000000000000002a, which is the hexadecimal representation of the decimal number 42, right-aligned to 8 bytes. Encoding u128::MAX - 1 yields: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, it's right-aligned to 16 bytes

Boolean

enc(X) is 0 if X is false or 1 if X is true, left-padded with zero-bytes. Total length must be 8 bytes. Similar to the u8 encoding.

Example:

Encoding true yields:

0x0000000000000001

B256

b256 is a fixed size bit array of length 256. Used for 256-bit hash digests and other 256-bit types. It is encoded as-is.

Example:

Encoding 0xc7fd1d987ada439fc085cfa3c49416cf2b504ac50151e3c2335d60595cb90745 yields:

0xc7fd1d987ada439fc085cfa3c49416cf2b504ac50151e3c2335d60595cb90745

Address

A 256-bit (32-byte) address, encoded in the same way as a b256 argument: encoded as-is.

Example:

Encoding 0xc7fd1d987ada439fc085cfa3c49416cf2b504ac50151e3c2335d60595cb90745 yields:

0xc7fd1d987ada439fc085cfa3c49416cf2b504ac50151e3c2335d60595cb90745

Array

An array of a certain type T, [T; n], where n is the length of the array.

Arrays in Sway have a fixed-length which is known at compile time. This means the ABI encoding for arrays also happens in-place, with no need to account for dynamic sizing.

The encoding for the array contains, in order, the encoding of each element in [T; n], recursively following the encoding for the type T.

For instance, consider the function signature my_func(bool, [u64; 2]) with the values (true, [1, 2]).

The encoding will be:

  1. 0x0000000000000001, the true bool encoded in-place.
  2. 0x0000000000000001, First element of the parameter [u64; 2], 1, encoded as a u64.
  3. 0x0000000000000002, Second element of the parameter [u64; 2], 2, encoded as a u64.

The resulting encoded ABI will be:

0x000000000000000100000000000000010000000000000002

Fixed-length Strings

Strings have fixed length and are encoded in-place. str[n], where n is the fixed-size of the string. Rather than padding the string, the encoding of the elements of the string is tightly packed. And unlike the other type encodings, the string encoding is left-aligned.

Note that all strings are encoded in UTF-8.

Example:

Encoding "Hello, World" as a str[12] yields:

0x48656c6c6f2c20576f726c6400000000

Note that we're padding with zeroes in order to keep it right-aligned to 8 bytes (FuelVM's word size).

Structs

Encoding ABIs that contain custom types, such as structs, is similar to encoding a set of primitive types. The encoding will proceed in the order that the inner types of a custom type are declared and recursively just like encoding any other type. This way you can encode structs with primitive types or structs with more complex types in them such as other structs, arrays, strings, and enums.

Here's an example:

#![allow(unused)]
fn main() {
struct InputStruct {
    field_1: bool,
    field_2: u8,
}


abi MyContract {
    fn foo(a: u64);
    fn bar(a: InputStruct);
} {
    fn baz(a: ()) { }
}
}

Calling bar with InputStruct { field_1: true, field_2: 5 } yields:

0x
0000000000000001 // `true` encoded as a bool
0000000000000005 // `5` encoded as u8

A more complex scenario:

#![allow(unused)]
fn main() {
struct InputStruct {
    field_1: bool,
    field_2: [u8; 2], // An array of u8, with length 2.
}


abi MyContract {
    fn foo(a: u64);
    fn bar(a: InputStruct);
} {
    fn baz(a: ()) { }
}
}

Calling bar with InputStruct { field_1: true, field_2: [1, 2] } yields:

0x
0000000000000001 // `true` encoded as a bool
0000000000000001 // `1` encoded as u8
0000000000000002 // `2` encoded as u8

Enums (sum types)

ABI calls containing enums (sum types) are encoded similarly to structs: encode the discriminant first, then recursively encode the type of the enum variant being passed to the function being called.

#![allow(unused)]
fn main() {
enum MySumType
{
    X: u32,
    Y: bool,
}

abi MyContract {
    fn foo(a: u64);
    fn bar(a: MySumType);
} {
    fn baz(a: ()) { }
}
}

Calling bar with MySumType::X(42) yields:

0x
0000000000000000 // The discriminant of the chosen enum, in this case `0`.
000000000000002a // `42` encoded as u64

If the sum type has variants of different sizes, then left padding is required.

#![allow(unused)]
fn main() {
enum MySumType
{
    X: b256,
    Y: u32,
}

abi MyContract {
    fn foo(a: u64);
    fn bar(a: MySumType);
} {
    fn baz(a: ()) { }
}
}

Calling bar with MySumType::Y(42) yields:

0x
0000000000000001 // The discriminant of the chosen enum, in this case `1`.
0000000000000000 // Left padding
0000000000000000 // Left padding
0000000000000000 // Left padding
000000000000002a // `42` encoded as u64

Note that three words of padding are required because the largest variant of MySumType is 4 words wide.

If all the variants of a sum type are of type (), or unit, then only the discriminant needs to be encoded.

#![allow(unused)]
fn main() {
enum MySumType
{
    X: (),
    Y: (),
    Z: (),
}

abi MyContract {
    fn foo(a: u64);
    fn bar(a: MySumType);
} {
    fn baz(a: ()) { }
}
}

Calling bar with MySumType::Z yields:

0x
0000000000000002 // The discriminant of the chosen enum, in this case `2`.

Vectors

ABI calls containing vectors are encoded in the following way:

  • First, figure out the the length l of the vector. Its length will also be its capacity.
  • Encode the content of the vector according to the spec of its type, e.g. for a Vec<bool>, encode each bool element according to the bool specs. This gives out data that is stored on the heap, and we encode only the pointer to this data
#![allow(unused)]
fn main() {
abi MyContract {
  fn foo(a: Vec<u32>);
} {
  fn foo(a: Vec<u32>) {};
}
}

Calling foo with vec![1u32, 2u32, 3u32, 4u32]:

  • length is 4, capacity is 4
  • data: [0x0000000000000001, 0x0000000000000002, 0x0000000000000003, 0x0000000000000004], stored at pointer address 0x000000000000beef

Note: to understand how the u32 are encoded, see the section about encoding integers.

0x
000000000000beef // pointer address
0000000000000004 // length = 4
0000000000000004 // capacity = 4

At the pointer address, then the vector's content are encoded as such:

0x
0000000000000001 // 1u32
0000000000000002 // 2u32
0000000000000003 // 3u32
0000000000000004 // 4u32

Tuples

ABI calls containing tuples are encoded as such: If X is a tuple with the type signature (T_1, T_2, ..., T_n), with T_1,...,T_n being any type except for vector, then enc(X) is encoded as the concatenation of enc(T_1), enc(T_2),enc (T_3), ..., enc(T_n).

#![allow(unused)]
fn main() {
abi MyContract {
  fn foo(a: (u64, str[3], bool));
} {
  fn foo(a: (u64, str[4], bool)) {};
}
}

Calling foo with (1u64, "fuel", true) :

0x
0000000000000001 // 1u64
6675656c00000000 // "fuel" encoded as per the specs
0000000000000001 // true

Version 1

This version was created to replace the older version 0 described above, and follows three philosophical tenets:

  • being self-sufficient: it must be possible to completely decode the original data only using the encoded bytes and the original type (there are no references to data outside the encoded bytes);
  • no overhead: only the bare minimum bytes are necessary to do the encoding. No metadata, headers, etc...;
  • no relation with runtime memory layout: no padding, no alignment, etc...

Primitive Types

Primitive types will be encoded using the exact number of bits they need:

  • u8: 1 byte;
  • u16: 2 bytes;
  • u32: 4 bytes;
  • u64: 8 bytes;
  • u256: 32 bytes;
  • b256: 32 bytes;

Arrays

Arrays are encoded without any padding or alignment, with one item after the other.

  • [T; 1] is encoded [encode(T)];
  • [T; 2] is encoded [encode(T), encode(T)]

Strings

String arrays are encoded just like arrays, without any overhead.

  • str[1] = 1 byte
  • str[2] = 2 bytes
  • str[n] = n bytes

String slices do contain their length as u64, and the string itself is encoded packed without alignment or padding.

  • "abc" = [0, 0, 0, 0, 0, 0, 0, 3, "a", "b", "c"]

Slices

raw_slice, also being dynamic, contains their length as u64 and is treated as a "slice of bytes". Each byte is encoded as u8 (1 byte) and is packed without alignment and padding.

For example, a slice of three bytes like [0u8, 1u8, 2u8] will be encoded as bytes [0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 2]

Tuple

Tuples are encoded just like arrays, without any overhead like padding and alignment:

  • (A, B, C) = [encode(A), encode(B), encode(C)]

Structs (v1)

Structs can be encoded in two ways:

  • first, with the automatic implementation;
  • second, with the custom implementation.

Auto implementation follows the same rules as tuples. So we can imagine that

struct S {
    a: A,
    b: B,
    c: C
}

is encoded the same way as the tuple (A, B, C).

Custom implementation allows the developer to choose how a struct is encoded.

A struct has auto-implemented encoding if no custom was found.

Enums

Enums can also be encoded with the automatic or the custom implementation.

The auto implementation first encoded the variant with a u64 number starting from zero as the first variant and increments this value for each variant, following declaration order.

enum E {
    VARIANT_A: A, // <- variant 0
    VARIANT_B: B, // <- variant 1
    VARIANT_C: C  // <- variant 2 
}

will be encoded as [encode(variant), encode(value)].

The variant data will be encoded right after the variant tag, without any alignment or padding.

An enum has auto-implemented encoding if no custom was found.

Data Structures

Some common data structures also have well-defined encoding:

  • Vec will be encoded as [encode(length), <encode each item>]
  • Bytes will be encoded as [encode(length), <bytes>]
  • String will be encoded as [encode (length), <data>]

All of them first contain the length and then their data right after, without any padding or alignment.

Hash based IDs

Hash based ids are deterministically generated from associated types and are used in the JSON ABI for type IDs and for logId. This document specifies how the hash based IDS are generated for type IDs and for logId.

Generation

Hash based ids for type IDs are generated from the sha256 of a string that represents the type.

For logIds we use the first 8 bytes of the sha256 of a string that represents the type, this is because the LOG and LOGD opcodes use a 64bit value as log id.

String representation of types

For describing the string representation of type we will use the notation {abi_str(T)} that should be replaced by the respective ABI string representation of the respective type T.

Intrinsics

u8 => "u8" u16 => "u16" u32 => "u32" u64 => "u64" u256 => "u256" b256 => "b256" bool => "bool"

String arrays

String array of size 1 => "str[1]" String array of size 2 => "str[2]" etc.

String slices

String slice => "str"

Arrays

[T; 1] => "[{abi_str(T)}; 1]" [T; 2] => "[{abi_str(T)}; 2]" etc.

Tuples

() => "()" (T1) => "({abi_str(T1)})" (T1,T2) => "({abi_str(T1)}, {abi_str(T2)})" etc.

Enums

Option enum with type parameter T => "enum std::option::Option<{abi_str(T)}>" Enum without type parameters named MyEnum => "enum MyEnum" Enum with type parameter T1 named MyEnum => "enum MyEnum<{abi_str(T1)}>" Enum with type parameters T1, T2 named MyEnum in my_module => "enum my_module::MyEnum<{abi_str(T1)},{abi_str(T2)}>"

Structs

Vec struct with type parameter T => "struct std::vec::Vec<{abi_str(T)}>" Struct without type parameters named MyStruct => "struct MyStruct" Struct with type parameter T1 named MyStruct => "struct MyStruct<{abi_str(T1)}>" Struct with type parameters T1, T2 named MyStruct in my_module => "struct my_module::MyStruct<{abi_str(T1)},{abi_str(T2)}>"

Generic Type Parameter

Generic type parameter T if root type => "generic T" Generic type parameter T if not root type => "T" as in "struct MyStruct<T>"

Complex examples composition

Tuple of array and u64 => "([u64,1]; u64)" Array of Option<u64>=> "[enum std::option::Option<u64>; 3]" Struct with tuple type parameter => "struct my_module::MyStruct<(u64, u64)>"

Fuel VM Specification

Introduction

This document provides the specification for the Fuel Virtual Machine (FuelVM). The specification covers the types, instruction set, and execution semantics.

Parameters

nametypevaluenote
CONTRACT_MAX_SIZEuint64Maximum contract size, in bytes.
VM_MAX_RAMuint642**2664 MiB.
MESSAGE_MAX_DATA_SIZEuint64Maximum size of message data, in bytes.

Semantics

FuelVM instructions are exactly 32 bits (4 bytes) wide and comprise of a combination of:

  • Opcode: 8 bits
  • Register/special register (see below) identifier: 6 bits
  • Immediate value: 12, 18, or 24 bits, depending on operation

Of the 64 registers (6-bit register address space), the first 16 are reserved:

valueregisternamedescription
0x00$zerozeroContains zero (0), for convenience.
0x01$oneoneContains one (1), for convenience.
0x02$ofoverflowContains overflow/underflow of addition, subtraction, and multiplication.
0x03$pcprogram counterThe program counter. Memory address of the current instruction.
0x04$sspstack start pointerMemory address of bottom of current writable stack area.
0x05$spstack pointerMemory address on top of current writable stack area (points to free memory).
0x06$fpframe pointerMemory address of beginning of current call frame.
0x07$hpheap pointerMemory address below the current bottom of the heap (points to used/OOB memory).
0x08$errerrorError codes for particular operations.
0x09$ggasglobal gasRemaining gas globally.
0x0A$cgascontext gasRemaining gas in the context.
0x0B$balbalanceReceived balance for this context.
0x0C$isinstructions startPointer to the start of the currently-executing code.
0x0D$retreturn valueReturn value or pointer.
0x0E$retlreturn lengthReturn value length in bytes.
0x0F$flagflagsFlags register.

Integers are represented in big-endian format, and all operations are unsigned. Boolean false is 0 and Boolean true is 1.

Registers are 64 bits (8 bytes) wide. Words are the same width as registers.

Persistent state (i.e. storage) is a key-value store with 32-byte keys and 32-byte values. Each contract has its own persistent state that is independent of other contracts. This is committed to in a Sparse Binary Merkle Tree.

Flags

valuenamedescription
0x01F_UNSAFEMATHIf set, undefined arithmetic zeroes target and sets $err without panic.
0x02F_WRAPPINGIf set, overflowing arithmetic wraps around and sets $of without panic.

All other flags are reserved, any must be set to zero.

Instruction Set

A complete instruction set of the Fuel VM is documented in the following page.

VM Initialization

Every time the VM runs, a single monolithic memory of size VM_MAX_RAM bytes is allocated, indexed by individual byte. A stack and heap memory model is used, allowing for dynamic memory allocation in higher-level languages. The stack begins at 0 and grows upward. The heap begins at VM_MAX_RAM and grows downward.

To initialize the VM, the following is pushed on the stack sequentially:

  1. Transaction hash (byte[32], word-aligned), computed as defined here.
  2. Base asset ID (byte[32], word-aligned)
  3. MAX_INPUTS pairs of (asset_id: byte[32], balance: uint64), of:
    1. For predicate estimation and predicate verification, zeroes.
    2. For script execution, the free balance for each asset ID seen in the transaction's inputs, ordered in ascending order. If there are fewer than MAX_INPUTS asset IDs, the pair has a value of zero.
  4. Transaction length, in bytes (uint64, word-aligned).
  5. The transaction, serialized.

Then the following registers are initialized (without explicit initialization, all registers are initialized to zero):

  1. $ssp = 32 + 32 + MAX_INPUTS*(32+8) + size(tx)): the writable stack area starts immediately after the serialized transaction in memory (see above).
  2. $sp = $ssp: writable stack area is empty to start.
  3. $hp = VM_MAX_RAM: the heap area begins at the top and is empty to start.

Contexts

There are 4 contexts in the FuelVM: predicate estimation, predicate verification, scripts, and calls. A context is an isolated execution environment with defined memory ownership and can be external or internal:

  • External: predicate and script. $fp will be zero.
  • Internal: call. $fp will be non-zero.

Returning from a context behaves differently depending on whether the context is external or internal.

Predicate Estimation

For any input of type InputType.Coin or InputType.Message, a non-zero predicateLength field means the UTXO being spent is a P2SH rather than a P2PKH output.

For each such input in the transaction, the VM is initialized, then:

  1. $pc and $is are set to the start of the input's predicate field.
  2. $ggas and $cgas are set to MAX_GAS_PER_PREDICATE.

Predicate estimation will fail if gas is exhausted during execution.

During predicate mode, hitting any of the following instructions causes predicate estimation to halt, returning Boolean false:

  1. Any contract instruction.

In addition, during predicate mode if $pc is set to a value greater than the end of predicate bytecode (this would allow bytecode outside the actual predicate), predicate estimation halts returning Boolean false.

A predicate that halts without returning Boolean true would not pass verification, making the entire transaction invalid. Note that predicate validity is monotonic with respect to time (i.e. if a predicate evaluates to true then it will always evaluate to true in the future).

After successful execution, predicateGasUsed is set to MAX_GAS_PER_PREDICATE - $ggas.

Predicate Verification

For any input of type InputType.Coin or InputType.Message, a non-zero predicateLength field means the UTXO being spent is a P2SH rather than a P2PKH output.

For each such input in the transaction, the VM is initialized, then:

  1. $pc and $is are set to the start of the input's predicate field.
  2. $ggas and $cgas are set to predicateGasUsed.

Predicate verification will fail if gas is exhausted during execution.

During predicate mode, hitting any contract instruction causes predicate verification to halt, returning Boolean false.

In addition, during predicate mode if $pc is set to a value greater than the end of predicate bytecode (this would allow bytecode outside the actual predicate), predicate verification halts returning Boolean false.

A predicate that halts without returning Boolean true does not pass verification, making the entire transaction invalid. Note that predicate validity is monotonic with respect to time (i.e. if a predicate evaluates to true then it will always evaluate to true in the future).

After execution, if $ggas is non-zero, predicate verification fails.

Script Execution

If script bytecode is present, transaction validation requires execution.

The VM is initialized, then:

  1. $pc and $is are set to the start of the transaction's script bytecode.
  2. $ggas and $cgas are set to tx.scriptGasLimit.

Following initialization, execution begins.

For each instruction, its gas cost gc is first computed. If gc > $cgas, deduct $cgas from $ggas and $cgas (i.e. spend all of $cgas and no more), then revert immediately without actually executing the instruction. Otherwise, deduct gc from $ggas and $cgas.

After the script has been executed, tx.receiptsRoot is updated to contain the Merkle root of the receipts, as described in the TransactionScript spec.

Call Frames

Cross-contract calls push a call frame onto the stack, similar to a stack frame used in regular languages for function calls (which may be used by a high-level language that targets the FuelVM). The distinction is as follows:

  1. Stack frames: store metadata across trusted internal (i.e. intra-contract) function calls. Not supported natively by the FuelVM, but may be used as an abstraction at a higher layer.
  2. Call frames: store metadata across untrusted external (i.e. inter-contract) calls. Supported natively by the FuelVM.

Call frames are needed to ensure that the called contract cannot mutate the running state of the current executing contract. They segment access rights for memory: the currently-executing contracts may only write to their own call frame and their own heap.

A call frame consists of the following, word-aligned:

bytestypevaluedescription
Unwritable area begins.
32byte[32]toContract ID for this call.
32byte[32]asset_idasset ID of forwarded coins.
8*64byte[8][64]regsSaved registers from previous context.
8uint64codesizeCode size in bytes, padded to the next word boundary.
8byte[8]param1First parameter.
8byte[8]param2Second parameter.
1*byte[]codeZero-padded to 8-byte alignment, but individual instructions are not aligned.
Unwritable area ends.
*Call frame's stack.

Access rights

Only memory that has been allocated is accessible. In other words, memory between highest-ever $sp value and current $hp is inaccessible. Attempting to read or write memory that has not been allocated will result in VM panic. Similarly reads or writes that cross from the stack to the heap will panic. Note that stack remains readable even after stack frame has been shrunk. However, if the heap is afterwards expanded to cover that area, the crossing read prohibition still remains, while all memory is accessible.

Ownership

Whenever memory is written to (i.e. with SB or SW), or write access is granted (i.e. with CALL), ownership must be checked.

If the context is external, the owned memory range is:

  1. [$ssp, $sp): the writable stack area.
  2. [$hp, VM_MAX_RAM): the heap area allocated by this script or predicate.

If the context is internal, the owned memory range for a call frame is:

  1. [$ssp, $sp): the writable stack area of the call frame.
  2. [$hp, $fp->$hp): the heap area allocated by this call frame.

Executability

Memory is only executable in range [$is, $ssp). Attempting to execute instructions outside these boundaries will cause a panic. This area never overlaps with writable memory, essentially providing W^X protection.

FuelVM Instruction Set

Reading Guide

This page provides a description of all instructions for the FuelVM. Encoding is read as a sequence of one 8-bit value (the opcode identifier) followed by four 6-bit values (the register identifiers or immediate value). A single i indicates a 6-bit immediate value, i i indicates a 12-bit immediate value, i i i indicates an 18-bit immediate value, and i i i i indicates a 24-bit immediate value. All immediate values are interpreted as big-endian unsigned integers. If the instruction would be shorter than the full 32 bits, the remaining part is reserved and must be zero.

  • The syntax MEM[x, y] used in this page means the memory range starting at byte x, of length y bytes.
  • The syntax STATE[x, y] used in this page means the sequence of storage slots starting at key x and spanning y bytes.

Panics

Some instructions may panic, i.e. enter an unrecoverable state. Additionally, attempting to execute an instruction not in this list causes a panic and consumes no gas. Instructions with reserved part having a non-zero value will likewise panic. How a panic is handled depends on context:

  • In a predicate context, cease VM execution and return false.
  • In other contexts, revert (described below).

On a non-predicate panic, append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.Panic
idbyte[32]Contract ID of current context if in an internal context, zero otherwise.
pcuint64Value of register $pc.
isuint64Value of register $is.

then append an additional receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.ScriptResult
resultuint641
gas_useduint64Gas consumed by the script.

Receipts

The number of receipts is limited to 216, with the last two reserved to panic and script result receipts. Trying to add any other receipts after 216-2 will panic.

Effects

A few instructions are annotated with the effects they produce, the table below explains each effect:

effect namedescription
Storage readInstruction reads from storage slots
Storage writeInstruction writes to storage slots
External callExternal contract call instruction
Balance tree readInstruction reads from the balance tree
Balance tree writeInstruction writes to the balance tree
Output messageInstruction sends a message to a recipient address

If an instruction is not annotated with an effect, it means it does not produce any of the aforementioned affects.

Arithmetic/Logic (ALU) Instructions

All these instructions advance the program counter $pc by 4 after performing their operation.

Normally, if the result of an ALU operation is mathematically undefined (e.g. dividing by zero), the VM panics. However, if the F_UNSAFEMATH flag is set, $err is set to true and execution continues.

If an operation would overflow, so that the result doesn't fit into the target field, the VM will panic. Results below zero are also considered overflows. If the F_WRAPPING flag is set, instead $of is set to true or the overflowing part of the result, depending on the operation.

ADD: Add

DescriptionAdds two registers.
Operation$rA = $rB + $rC;
Syntaxadd $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

$of is assigned the overflow of the operation.

$err is cleared.

ADDI: Add immediate

DescriptionAdds a register and an immediate value.
Operation$rA = $rB + imm;
Syntaxaddi $rA, $rB, immediate
Encoding0x00 rA rB i i
Notes

Panic if:

$of is assigned the overflow of the operation.

$err is cleared.

AND: AND

DescriptionBitwise ANDs two registers.
Operation$rA = $rB & $rC;
Syntaxand $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

$of and $err are cleared.

ANDI: AND immediate

DescriptionBitwise ANDs a register and an immediate value.
Operation$rA = $rB & imm;
Syntaxandi $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

imm is extended to 64 bits, with the high 52 bits set to 0.

$of and $err are cleared.

DIV: Divide

DescriptionDivides two registers.
Operation$rA = $rB // $rC;
Syntaxdiv $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

If $rC == 0, $rA is cleared and $err is set to true.

Otherwise, $err is cleared.

$of is cleared.

DIVI: Divide immediate

DescriptionDivides a register and an immediate value.
Operation$rA = $rB // imm;
Syntaxdivi $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

If imm == 0, $rA is cleared and $err is set to true.

Otherwise, $err is cleared.

$of is cleared.

EQ: Equals

DescriptionCompares two registers for equality.
Operation$rA = $rB == $rC;
Syntaxeq $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

$of and $err are cleared.

EXP: Exponentiate

DescriptionRaises one register to the power of another.
Operation$rA = $rB ** $rC;
Syntaxexp $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

If the result cannot fit in 8 bytes, $of is set to 1 and $rA is instead set to 0, otherwise $of is cleared.

$err is cleared.

EXPI: Exponentiate immediate

DescriptionRaises one register to the power of an immediate value.
Operation$rA = $rB ** imm;
Syntaxexpi $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

If the result cannot fit in 8 bytes, $of is set to 1 and $rA is instead set to 0, otherwise $of is cleared.

$err is cleared.

GT: Greater than

DescriptionCompares two registers for greater-than.
Operation$rA = $rB > $rC;
Syntaxgt $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

$of and $err are cleared.

LT: Less than

DescriptionCompares two registers for less-than.
Operation$rA = $rB < $rC;
Syntaxlt $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

$of and $err are cleared.

MLOG: Math logarithm

DescriptionThe (integer) logarithm base $rC of $rB.
Operation$rA = math.floor(math.log($rB, $rC));
Syntaxmlog $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

If $rB == 0, both $rA and $of are cleared and $err is set to true.

If $rC <= 1, both $rA and $of are cleared and $err is set to true.

Otherwise, $of and $err are cleared.

MOD: Modulus

DescriptionModulo remainder of two registers.
Operation$rA = $rB % $rC;
Syntaxmod $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

If $rC == 0, both $rA and $of are cleared and $err is set to true.

Otherwise, $of and $err are cleared.

MODI: Modulus immediate

DescriptionModulo remainder of a register and an immediate value.
Operation$rA = $rB % imm;
Syntaxmodi $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

If imm == 0, both $rA and $of are cleared and $err is set to true.

Otherwise, $of and $err are cleared.

MOVE: Move

DescriptionCopy from one register to another.
Operation$rA = $rB;
Syntaxmove $rA, $rB
Encoding0x00 rA rB - -
Notes

Panic if:

$of and $err are cleared.

MOVI: Move immediate

DescriptionCopy an immediate value into a register.
Operation$rA = imm;
Syntaxmovi $rA, imm
Encoding0x00 rA i i i
Notes

Panic if:

$of and $err are cleared.

MROO: Math root

DescriptionThe (integer) $rCth root of $rB.
Operation$rA = math.floor(math.root($rB, $rC));
Syntaxmroo $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

If $rC == 0, both $rA and $of are cleared and $err is set to true.

Otherwise, $of and $err are cleared.

MUL: Multiply

DescriptionMultiplies two registers.
Operation$rA = $rB * $rC;
Syntaxmul $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

$of is assigned the overflow of the operation.

$err is cleared.

MULI: Multiply immediate

DescriptionMultiplies a register and an immediate value.
Operation$rA = $rB * imm;
Syntaxmul $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

$of is assigned the overflow of the operation.

$err is cleared.

MLDV: Fused multiply-divide

DescriptionMultiplies two registers with arbitrary precision, then divides by a third register.
Operationa = (b * c) / d;
Syntaxmldv $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
NotesDivision by zero is treated as division by 1 << 64 instead.

If the divisor ($rD) is zero, then instead the value is divided by 1 << 64. This returns the higher half of the 128-bit multiplication result. This operation never overflows.

If the result of after the division doesn't fit into a register, $of is assigned the overflow of the operation. Otherwise, $of is cleared.

$err is cleared.

NOOP: No operation

DescriptionPerforms no operation.
Operation
Syntaxnoop
Encoding0x00 - - - -
Notes

$of and $err are cleared.

NOT: Invert

DescriptionBitwise NOT a register.
Operation$rA = ~$rB;
Syntaxnot $rA, $rB
Encoding0x00 rA rB - -
Notes

Panic if:

$of and $err are cleared.

OR: OR

DescriptionBitwise ORs two registers.
Operation$rA = $rB | $rC;
Syntaxor $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

$of and $err are cleared.

ORI: OR immediate

DescriptionBitwise ORs a register and an immediate value.
Operation$rA = $rB | imm;
Syntaxori $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

imm is extended to 64 bits, with the high 52 bits set to 0.

$of and $err are cleared.

SLL: Shift left logical

DescriptionLeft shifts a register by a register.
Operation$rA = $rB << $rC;
Syntaxsll $rA, $rB, $rC
Encoding0x00 rA rB rC -
NotesZeroes are shifted in.

Panic if:

$of and $err are cleared.

SLLI: Shift left logical immediate

DescriptionLeft shifts a register by an immediate value.
Operation$rA = $rB << imm;
Syntaxslli $rA, $rB, imm
Encoding0x00 rA rB i i
NotesZeroes are shifted in.

Panic if:

$of and $err are cleared.

SRL: Shift right logical

DescriptionRight shifts a register by a register.
Operation$rA = $rB >> $rC;
Syntaxsrl $rA, $rB, $rC
Encoding0x00 rA rB rC -
NotesZeroes are shifted in.

Panic if:

$of and $err are cleared.

SRLI: Shift right logical immediate

DescriptionRight shifts a register by an immediate value.
Operation$rA = $rB >> imm;
Syntaxsrli $rA, $rB, imm
Encoding0x00 rA rB i i
NotesZeroes are shifted in.

Panic if:

$of and $err are cleared.

SUB: Subtract

DescriptionSubtracts two registers.
Operation$rA = $rB - $rC;
Syntaxsub $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes$of is assigned the overflow of the operation.

Panic if:

$of is assigned the underflow of the operation, as though $of is the high byte of a 128-bit register.

$err is cleared.

SUBI: Subtract immediate

DescriptionSubtracts a register and an immediate value.
Operation$rA = $rB - imm;
Syntaxsubi $rA, $rB, imm
Encoding0x00 rA rB i i
Notes$of is assigned the overflow of the operation.

Panic if:

$of is assigned the underflow of the operation, as though $of is the high byte of a 128-bit register.

$err is cleared.

WDCM: 128-bit integer comparison

DescriptionCompare or examine two 128-bit integers using selected mode
Operationb = mem[$rB,16];
c = indirect?mem[$rC,16]:$rC;
$rA = cmp_op(b,c);
Syntaxwdcm $rA, $rB, $rC, imm
Encoding0x00 rA rB rC i
Notes

The six-bit immediate value is used to select operating mode, as follows:

BitsShort nameDescription
...XXXmodeCompare mode selection
.XX...reservedReserved and must be zero
X.....indirectIs rhs operand ($rC) indirect or not

Then the actual operation that's performed:

modeNameDescription
0eqEquality (==)
1neInequality (!=)
2ltLess than (<)
3gtGreater than (>)
4lteLess than or equals (<=)
5gteGreater than or equals (>=)
6lzcLeading zero count the lhs argument (lzcnt). Discards rhs.
7-Reserved and must not be used

The leading zero count can be used to compute rounded-down log2 of a number using the following formula TOTAL_BITS - 1 - lzc(n). Note that log2(0) is undefined, and will lead to integer overflow with this method.

Clears $of and $err.

Panic if:

  • A reserved compare mode is given
  • $rA is a reserved register
  • $rB + 16 overflows or > VM_MAX_RAM
  • indirect == 1 and $rC + 16 overflows or > VM_MAX_RAM

WQCM: 256-bit integer comparison

DescriptionCompare or examine two 256-bit integers using selected mode
Operationb = mem[$rB,32];
c = indirect?mem[$rC,32]:$rC;
$rA = cmp_op(b,c);
Syntaxwqcm $rA, $rB, $rC, imm
Encoding0x00 rA rB rC i
Notes

The immediate value is interpreted identically to WDCM.

Clears $of and $err.

Panic if:

  • A reserved compare mode is given
  • $rA is a reserved register
  • $rB + 32 overflows or > VM_MAX_RAM
  • indirect == 1 and $rC + 32 overflows or > VM_MAX_RAM

WDOP: Misc 128-bit integer operations

DescriptionPerform an ALU operation on two 128-bit integers
Operationb = mem[$rB,16];
c = indirect?mem[$rC,16]:$rC;
mem[$rA,16] = op(b,c);
Syntaxwdop $rA, $rB, $rC, imm
Encoding0x00 rA rB rC i
Notes

The six-bit immediate value is used to select operating mode, as follows:

BitsShort nameDescription
...XXXopOperation selection, see below
.XX...reservedReserved and must be zero
X.....indirectIs rhs operand ($rC) indirect or not

Then the actual operation that's performed:

opNameDescription
0addAdd
1subSubtract
2notInvert bits (discards rhs)
3orBitwise or
4xorBitwise exclusive or
5andBitwise and
6shlShift left (logical)
7shrShift right (logical)

Operations behave $of and $err similarly to their 64-bit counterparts, except that $of is set to 1 instead of the overflowing part.

Panic if:

  • Reserved bits of the immediate are set
  • The memory range MEM[$rA, 16] does not pass ownership check
  • $rB + 16 overflows or > VM_MAX_RAM
  • indirect == 1 and $rC + 16 overflows or > VM_MAX_RAM

WQOP: Misc 256-bit integer operations

DescriptionPerform an ALU operation on two 256-bit integers
Operationb = mem[$rB,32];
c = indirect?mem[$rC,32]:$rC;
mem[$rA,32] = op(b,c);
Syntaxwqop $rA, $rB, $rC, imm
Encoding0x00 rA rB rC i
Notes

The immediate value is interpreted identically to WDOP.

Operations behave $of and $err similarly to their 64-bit counterparts.

Panic if:

  • Reserved bits of the immediate are set
  • The memory range MEM[$rA, 32] does not pass ownership check
  • $rB + 32 overflows or > VM_MAX_RAM
  • indirect == 1 and $rC + 32 overflows or > VM_MAX_RAM

WDML: Multiply 128-bit integers

DescriptionPerform integer multiplication operation on two 128-bit integers.
Operationb=indirect0?mem[$rB,16]:$rB;
c=indirect1?mem[$rC,16]:$rC;
mem[$rA,16]=b*c;
Syntaxwdml $rA, $rB, $rC, imm
Encoding0x00 rA rB rC i
Notes

The six-bit immediate value is used to select operating mode, as follows:

BitsShort nameDescription
..XXXXreservedReserved and must be zero
.X....indirect0Is lhs operand ($rB) indirect or not
X.....indirect1Is rhs operand ($rC) indirect or not

$of is set to 1 in case of overflow, and cleared otherwise.

$err is cleared.

Panic if:

  • Reserved bits of the immediate are set
  • The memory range MEM[$rA, 16] does not pass ownership check
  • indirect0 == 1 and $rB + 16 overflows or > VM_MAX_RAM
  • indirect1 == 1 and $rC + 16 overflows or > VM_MAX_RAM

WQML: Multiply 256-bit integers

DescriptionPerform integer multiplication operation on two 256-bit integers.
Operationb=indirect0?mem[$rB,32]:$rB;
c=indirect1?mem[$rC,32]:$rC;
mem[$rA,32]=b*c;
Syntaxwqml $rA, $rB, $rC, imm
Encoding0x00 rA rB rC i
Notes

The immediate value is interpreted identically to WDML.

$of is set to 1 in case of overflow, and cleared otherwise.

$err is cleared.

Panic if:

  • Reserved bits of the immediate are set
  • The memory range MEM[$rA, 32] does not pass ownership check
  • indirect0 == 1 and $rB + 32 overflows or > VM_MAX_RAM
  • indirect1 == 1 and $rC + 32 overflows or > VM_MAX_RAM

WDDV: 128-bit integer division

DescriptionDivide a 128-bit integer by another.
Operationb = mem[$rB,16];
c = indirect?mem[$rC,16]:$rC;
mem[$rA,16] = b / c;
Syntaxwddv $rA, $rB, $rC, imm
Encoding0x00 rA rB rC i
Notes

The six-bit immediate value is used to select operating mode, as follows:

BitsShort nameDescription
.XXXXXreservedReserved and must be zero
X.....indirectIs rhs operand ($rC) indirect or not

$of is cleared.

If the rhs operand is zero, MEM[$rA, 16] is cleared and $err is set to true. Otherwise, $err is cleared.

Panic if:

  • Reserved bits of the immediate are set
  • The memory range MEM[$rA, 16] does not pass ownership check
  • $rB + 16 overflows or > VM_MAX_RAM
  • indirect == 1 and $rC + 16 overflows or > VM_MAX_RAM

WQDV: 256-bit integer division

DescriptionDivide a 256-bit integer by another.
Operationb = mem[$rB,32];
c = indirect?mem[$rC,32]:$rC;
mem[$rA,32] = b / c;
Syntaxwqdv $rA, $rB, $rC, imm
Encoding0x00 rA rB rC i
Notes

The immediate value is interpreted identically to WDDV.

$of is cleared.

If the rhs operand is zero, MEM[$rA, 32] is cleared and $err is set to true. Otherwise, $err is cleared.

Panic if:

  • Reserved bits of the immediate are set
  • The memory range MEM[$rA, 32] does not pass ownership check
  • $rB + 32 overflows or > VM_MAX_RAM
  • indirect == 1 and $rC + 32 overflows or > VM_MAX_RAM

WDMD: 128-bit integer fused multiply-divide

DescriptionCombined multiply-divide of 128-bit integers with arbitrary precision.
Operationb=mem[$rB,16];
c=mem[$rC,16];
d=mem[$rD,16];
mem[$rA,16]=(b * c) / d;
Syntaxwdmd $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
NotesDivision by zero is treated as division by 1 << 128 instead.

If the divisor MEM[$rA, 16] is zero, then instead the value is divided by 1 << 128. This returns the higher half of the 256-bit multiplication result.

If the result of after the division is larger than operand size, $of is set to one. Otherwise, $of is cleared.

$err is cleared.

Panic if:

  • The memory range MEM[$rA, 16] does not pass ownership check
  • $rB + 16 overflows or > VM_MAX_RAM
  • $rC + 16 overflows or > VM_MAX_RAM
  • $rD + 16 overflows or > VM_MAX_RAM

WQMD: 256-bit integer fused multiply-divide

DescriptionCombined multiply-divide of 256-bit integers with arbitrary precision.
Operationb=mem[$rB,32];
c=mem[$rC,32];
d=mem[$rD,32];
mem[$rA,32]=(b * c) / d;
Syntaxwqmd $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
NotesDivision by zero is treated as division by 1 << 256 instead.

If the divisor MEM[$rA, 32] is zero, then instead the value is divided by 1 << 256. This returns the higher half of the 512-bit multiplication result.

If the result of after the division is larger than operand size, $of is set to one. Otherwise, $of is cleared.

$err is cleared.

Panic if:

  • The memory range MEM[$rA, 32] does not pass ownership check
  • $rB + 32 overflows or > VM_MAX_RAM
  • $rC + 32 overflows or > VM_MAX_RAM
  • $rD + 32 overflows or > VM_MAX_RAM

WDAM: Modular 128-bit integer addition

DescriptionAdd two 128-bit integers and compute modulo remainder with arbitrary precision.
Operationb=mem[$rB,16];
c=mem[$rC,16];
d=mem[$rD,16];
mem[$rA,16] = (b+c)%d;
Syntaxwdam $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
Notes

$of is cleared.

If the rhs operand is zero, MEM[$rA, 16] is cleared and $err is set to true. Otherwise, $err is cleared.

Panic if:

  • The memory range MEM[$rA, 16] does not pass ownership check
  • $rB + 16 overflows or > VM_MAX_RAM
  • $rC + 16 overflows or > VM_MAX_RAM
  • $rD + 16 overflows or > VM_MAX_RAM

WQAM: Modular 256-bit integer addition

DescriptionAdd two 256-bit integers and compute modulo remainder with arbitrary precision.
Operationb=mem[$rB,32];
c=mem[$rC,32];
d=mem[$rD,32];
mem[$rA,32] = (b+c)%d;
Syntaxwqam $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
Notes

$of is cleared.

If the rhs operand is zero, MEM[$rA, 32] is cleared and $err is set to true. Otherwise, $err is cleared.

Panic if:

  • The memory range MEM[$rA, 32] does not pass ownership check
  • $rB + 32 overflows or > VM_MAX_RAM
  • $rC + 32 overflows or > VM_MAX_RAM
  • $rD + 32 overflows or > VM_MAX_RAM

WDMM: Modular 128-bit integer multiplication

DescriptionMultiply two 128-bit integers and compute modulo remainder with arbitrary precision.
Operationb=mem[$rB,16];
c=mem[$rC,16];
d=mem[$rD,16];
mem[$rA,16] = (b*c)%d;
Syntaxwdmm $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
Notes

$of is cleared.

If the rhs operand is zero, MEM[$rA, 16] is cleared and $err is set to true. Otherwise, $err is cleared.

Panic if:

  • The memory range MEM[$rA, 16] does not pass ownership check
  • $rB + 16 overflows or > VM_MAX_RAM
  • $rC + 16 overflows or > VM_MAX_RAM
  • $rD + 16 overflows or > VM_MAX_RAM

WQMM: Modular 256-bit integer multiplication

DescriptionMultiply two 256-bit integers and compute modulo remainder with arbitrary precision.
Operationb=mem[$rB,32];
c=mem[$rC,32];
d=mem[$rD,32];
mem[$rA,32] = (b*c)%d;
Syntaxwqmm $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
Notes

$of is cleared.

If the rhs operand is zero, MEM[$rA, 32] is cleared and $err is set to true. Otherwise, $err is cleared.

Panic if:

  • The memory range MEM[$rA, 32] does not pass ownership check
  • $rB + 32 overflows or > VM_MAX_RAM
  • $rC + 32 overflows or > VM_MAX_RAM
  • $rD + 32 overflows or > VM_MAX_RAM

XOR: XOR

DescriptionBitwise XORs two registers.
Operation$rA = $rB ^ $rC;
Syntaxxor $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

$of and $err are cleared.

XORI: XOR immediate

DescriptionBitwise XORs a register and an immediate value.
Operation$rA = $rB ^ imm;
Syntaxxori $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

$of and $err are cleared.

Control Flow Instructions

JMP: Jump

DescriptionJumps to the code instruction offset by a register.
Operation$pc = $is + $rA * 4;
Syntaxjmp $rA
Encoding0x00 rA - - -
Notes

Panic if:

  • $is + $rA * 4 > VM_MAX_RAM - 1

JI: Jump immediate

DescriptionJumps to the code instruction offset by imm.
Operation$pc = $is + imm * 4;
Syntaxji imm
Encoding0x00 i i i i
Notes

Panic if:

  • $is + imm * 4 > VM_MAX_RAM - 1

JNE: Jump if not equal

DescriptionJump to the code instruction offset by a register if $rA is not equal to $rB.
Operationif $rA != $rB:
$pc = $is + $rC * 4;
else:
$pc += 4;
Syntaxjne $rA $rB $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

  • $is + $rC * 4 > VM_MAX_RAM - 1 and the jump would be performed (i.e. $rA != $rB)

JNEI: Jump if not equal immediate

DescriptionJump to the code instruction offset by imm if $rA is not equal to $rB.
Operationif $rA != $rB:
$pc = $is + imm * 4;
else:
$pc += 4;
Syntaxjnei $rA $rB imm
Encoding0x00 rA rB i i
Notes

Panic if:

  • $is + imm * 4 > VM_MAX_RAM - 1 and the jump would be performed (i.e. $rA != $rB)

JNZI: Jump if not zero immediate

DescriptionJump to the code instruction offset by imm if $rA is not equal to $zero.
Operationif $rA != $zero:
$pc = $is + imm * 4;
else:
$pc += 4;
Syntaxjnzi $rA imm
Encoding0x00 rA i i i
Notes

Panic if:

  • $is + imm * 4 > VM_MAX_RAM - 1and the jump would be performed (i.e. $rA != $zero)

JMPB: Jump relative backwards

DescriptionJump $rA + imm instructions backwards.
Operation$pc -= ($rA + imm + 1) * 4;
Syntaxjmpb $rA imm
Encoding0x00 rA i i i
Notes

Panic if:

  • $pc - ($rA + imm + 1) * 4 < 0

JMPF: Jump relative forwards

DescriptionJump $rA + imm instructions forwards
Operation$pc += ($rA + imm + 1) * 4;
Syntaxjmpf $rA imm
Encoding0x00 rA i i i
Notes

Panic if:

  • $pc + ($rA + imm + 1) * 4 > VM_MAX_RAM - 1

JNZB: Jump if not zero relative backwards

DescriptionJump $rB + imm instructions backwards if $rA != $zero.
Operationif $rA != $zero:
$pc -= ($rB + imm + 1) * 4;
else:
$pc += 4;
Syntaxjnzb $rA $rB imm
Encoding0x00 rA rB i i
Notes

Panic if:

  • $pc - ($rB + imm + 1) * 4 < 0

JNZF: Jump if not zero relative forwards

DescriptionJump $rB + imm instructions forwards if $rA != $zero.
Operationif $rA != $zero:
$pc += ($rB + imm + 1) * 4;
else:
$pc += 4;
Syntaxjnzf $rA $rB imm
Encoding0x00 rA rB i i
Notes

Panic if:

  • $pc + ($rB + imm + 1) * 4 > VM_MAX_RAM - 1

JNEB: Jump if not equal relative backwards

DescriptionJump $rC + imm instructions backwards if $rA != $rB.
Operationif $rA != $rB:
$pc -= ($rC + imm + 1) * 4;
else:
$pc += 4;
Syntaxjneb $rA $rB $rC imm
Encoding0x00 rA rB rC i
Notes

Panic if:

  • $pc - ($rC + imm + 1) * 4 < 0

JNEF: Jump if not equal relative forwards

DescriptionJump $rC + imm instructions forwards if $rA != $rB.
Operationif $rA != $rB:
$pc += ($rC + imm + 1) * 4;
else:
$pc += 4;
Syntaxjnef $rA $rB $rC imm
Encoding0x00 rA rB rC i
Notes

Panic if:

  • $pc + ($rC + imm + 1) * 4 > VM_MAX_RAM - 1

RET: Return from context

DescriptionReturns from context with value $rA.
Operationreturn($rA);
Syntaxret $rA
Encoding0x00 rA - - -
Notes

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.Return
idbyte[32]Contract ID of current context if in an internal context, zero otherwise.
valuint64Value of register $rA.
pcuint64Value of register $pc.
isuint64Value of register $is.

If current context is external, append an additional receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.ScriptResult
resultuint640
gas_useduint64Gas consumed by the script.

If current context is external, cease VM execution and return $rA.

Returns from contract call, popping the call frame. Before popping perform the following operations.

Return the unused forwarded gas to the caller:

  1. $cgas = $cgas + $fp->$cgas (add remaining context gas from previous context to current remaining context gas)

Set the return value:

  1. $ret = $rA
  2. $retl = 0

Then pop the call frame and restore all registers except $ggas, $cgas, $ret, $retl and $hp. Afterwards, set the following registers:

  1. $pc = $pc + 4 (advance program counter from where we called)

Memory Instructions

All these instructions advance the program counter $pc by 4 after performing their operation.

ALOC: Allocate memory

DescriptionAllocate a number of bytes from the heap.
Operation$hp = $hp - $rA;
Syntaxaloc $rA
Encoding0x00 rA - - -
NotesNewly allocated memory is zeroed.

Panic if:

  • $hp - $rA underflows
  • $hp - $rA < $sp

CFE: Extend call frame

DescriptionExtend the current call frame's stack.
Operation$sp = $sp + $rA
Syntaxcfe $rA
Encoding0x00 rA - - -
NotesDoes not initialize memory.

Panic if:

  • $sp + $rA overflows
  • $sp + $rA > $hp

CFEI: Extend call frame immediate

DescriptionExtend the current call frame's stack by an immediate value.
Operation$sp = $sp + imm
Syntaxcfei imm
Encoding0x00 i i i i
NotesDoes not initialize memory.

Panic if:

  • $sp + imm overflows
  • $sp + imm > $hp

CFS: Shrink call frame

DescriptionShrink the current call frame's stack.
Operation$sp = $sp - $rA
Syntaxcfs $rA
Encoding0x00 $rA - - -
NotesDoes not clear memory.

Panic if:

  • $sp - $rA underflows
  • $sp - $rA < $ssp

CFSI: Shrink call frame immediate

DescriptionShrink the current call frame's stack by an immediate value.
Operation$sp = $sp - imm
Syntaxcfsi imm
Encoding0x00 i i i i
NotesDoes not clear memory.

Panic if:

  • $sp - imm underflows
  • $sp - imm < $ssp

LB: Load byte

DescriptionA byte is loaded from the specified address offset by imm.
Operation$rA = MEM[$rB + imm, 1];
Syntaxlb $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

LW: Load word

DescriptionA word is loaded from the specified address offset by imm.
Operation$rA = MEM[$rB + (imm * 8), 8];
Syntaxlw $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

MCL: Memory clear

DescriptionClear bytes in memory.
OperationMEM[$rA, $rB] = 0;
Syntaxmcl $rA, $rB
Encoding0x00 rA rB - -
Notes

Panic if:

  • $rA + $rB overflows or > VM_MAX_RAM
  • The memory range MEM[$rA, $rB] does not pass ownership check

MCLI: Memory clear immediate

DescriptionClear bytes in memory.
OperationMEM[$rA, imm] = 0;
Syntaxmcli $rA, imm
Encoding0x00 rA i i i
Notes

Panic if:

  • $rA + imm overflows or > VM_MAX_RAM
  • The memory range MEM[$rA, imm] does not pass ownership check

MCP: Memory copy

DescriptionCopy bytes in memory.
OperationMEM[$rA, $rC] = MEM[$rB, $rC];
Syntaxmcp $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

  • $rA + $rC overflows or > VM_MAX_RAM
  • $rB + $rC overflows or > VM_MAX_RAM
  • The memory ranges MEM[$rA, $rC] and MEM[$rB, $rC] overlap
  • The memory range MEM[$rA, $rC] does not pass ownership check

MCPI: Memory copy immediate

DescriptionCopy bytes in memory.
OperationMEM[$rA, imm] = MEM[$rB, imm];
Syntaxmcpi $rA, $rB, imm
Encoding0x00 rA rB imm imm
Notes

Panic if:

  • $rA + imm overflows or > VM_MAX_RAM
  • $rB + imm overflows or > VM_MAX_RAM
  • The memory ranges MEM[$rA, imm] and MEM[$rB, imm] overlap
  • The memory range MEM[$rA, imm] does not pass ownership check

MEQ: Memory equality

DescriptionCompare bytes in memory.
Operation$rA = MEM[$rB, $rD] == MEM[$rC, $rD];
Syntaxmeq $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
Notes

Panic if:

  • $rA is a reserved register
  • $rB + $rD overflows or > VM_MAX_RAM
  • $rC + $rD overflows or > VM_MAX_RAM

PSHH: Push a set of high registers to stack

DescriptionPush a set of registers from range 40..64 to the stack in order.
Operationtmp=$sp;
$sp+=popcnt(imm)*8;
MEM[tmp,$sp]=registers[40..64].mask(imm)
Syntaxpshh imm
Encoding0x00 i i i i
NotesThe immediate value is used as a bitmask for selecting the registers.

The nth bit of the bitmask corresponds to nth entry of the register range. In other words, the most significant (i.e. leftmost) bit of the bitmask corresponds to the highest register index. So for instance bitmask 011000000000000000000000 pushes the register 61 followed by register 62.

Panic if:

  • $sp + popcnt(imm)*8 overflows
  • $sp + popcnt(imm)*8 > $hp

PSHL: Push a set of low registers to stack

DescriptionPush a set of registers from range 16..40 to the stack in order.
Operationtmp=$sp;
$sp+=popcnt(imm)*8;
MEM[tmp,$sp]=registers[16..40].mask(imm)
Syntaxpshl imm
Encoding0x00 i i i i
NotesThe immediate value is used as a bitmask for selecting the registers.

The nth bit of the bitmask corresponds to nth entry of the register range. In other words, the most significant (i.e. leftmost) bit of the bitmask corresponds to the highest register index. So for instance bitmask 011000000000000000000000 pushes the register 37 followed by register 38.

Panic if:

  • $sp + popcnt(imm)*8 overflows
  • $sp + popcnt(imm)*8 > $hp

POPH: Pop a set of high registers from stack

DescriptionPop to a set of registers from range 40..64 from the stack.
Operationtmp=$sp-popcnt(imm)*8;
registers[40..64].mask(imm)=MEM[tmp,$sp]
$sp-=tmp;
Syntaxpoph imm
Encoding0x00 i i i i
NotesThe immediate value is used as a bitmask for selecting the registers.

The nth bit of the bitmask corresponds to nth entry of the register range. In other words, the most significant (i.e. leftmost) bit of the bitmask corresponds to the highest register index. So for instance bitmask 011000000000000000000000 pops the register 62 followed by register 61.

Note that the order is reverse from PSHH, so that PSHH a; POPH a returns to the original state.

Panic if:

  • $sp - popcnt(imm)*8 overflows
  • $sp - popcnt(imm)*8 < $ssp

POPL: Pop a set of low registers from stack

DescriptionPop to a set of registers from range 16..40 from the stack.
Operationtmp=$sp-popcnt(imm)*8;
registers[16..40].mask(imm)=MEM[tmp,$sp]
$sp-=tmp;
Syntaxpoph imm
Encoding0x00 i i i i
NotesThe immediate value is used as a bitmask for selecting the registers.

The nth bit of the bitmask corresponds to nth entry of the register range. In other words, the most significant (i.e. leftmost) bit of the bitmask corresponds to the highest register index. So for instance bitmask 011000000000000000000000 pops the register 38 followed by register 37.

Note that the order is reverse from PSHL, so that PSHL a; POPL a returns to the original state.

Panic if:

  • $sp - popcnt(imm)*8 overflows
  • $sp - popcnt(imm)*8 < $ssp

SB: Store byte

DescriptionThe least significant byte of $rB is stored at the address $rA offset by imm.
OperationMEM[$rA + imm, 1] = $rB[7, 1];
Syntaxsb $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

  • $rA + imm + 1 overflows or > VM_MAX_RAM
  • The memory range MEM[$rA + imm, 1] does not pass ownership check

SW: Store word

DescriptionThe value of $rB is stored at the address $rA offset by imm.
OperationMEM[$rA + (imm * 8), 8] = $rB;
Syntaxsw $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Panic if:

  • $rA + (imm * 8) + 8 overflows or > VM_MAX_RAM
  • The memory range MEM[$rA + (imm * 8), 8] does not pass ownership check

Contract Instructions

All these instructions advance the program counter $pc by 4 after performing their operation, except for CALL, RETD and RVRT.

BAL: Balance of contract ID

DescriptionSet $rA to the balance of asset ID at $rB for contract with ID at $rC.
Operation$rA = balance(MEM[$rB, 32], MEM[$rC, 32]);
Syntaxbal $rA, $rB, $rC
Encoding0x00 rA rB rC -
EffectsBalance tree read
Notes

Where helper balance(asset_id: byte[32], contract_id: byte[32]) -> uint64 returns the current balance of asset_id of contract with ID contract_id.

Panic if:

  • $rA is a reserved register
  • $rB + 32 overflows or > VM_MAX_RAM
  • $rC + 32 overflows or > VM_MAX_RAM
  • Contract with ID MEM[$rC, 32] is not in tx.inputs

BHEI: Block height

DescriptionGet Fuel block height.
Operation$rA = blockheight();
Syntaxbhei $rA
Encoding0x00 rA - - -
Notes

Panic if:

BHSH: Block hash

DescriptionGet block header hash.
OperationMEM[$rA, 32] = blockhash($rB);
Syntaxbhsh $rA $rB
Encoding0x00 rA rB - -
Notes

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • The memory range MEM[$rA, 32] does not pass ownership check

Block header hashes for blocks with height greater than or equal to current block height are zero (0x00**32).

BURN: Burn existing coins

DescriptionBurn $rA coins of the $rB ID from the current contract.
Operationburn($rA, $rB);
Syntaxburn $rA $rB
Encoding0x00 rA rB - -
Notes$rB is a pointer to a 32 byte ID in memory.

The asset ID is constructed using the asset ID construction method.

Panic if:

  • $rB + 32 overflows or > VM_MAX_RAM
  • Balance of asset ID from constructAssetID(MEM[$fp, 32], MEM[$rB, 32]) of output with contract ID MEM[$fp, 32] minus $rA underflows
  • $fp == 0 (in the script context)

For output with contract ID MEM[$fp, 32], decrease balance of asset ID constructAssetID(MEM[$fp, 32], MEM[$rB, 32]) by $rA.

This modifies the balanceRoot field of the appropriate output.

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.Burn
sub_idbyte[32]Asset sub identifier MEM[$rB, $rB + 32].
contract_idbyte[32]Contract ID of the current context.
valuint64Value of register $rA.
pcuint64Value of register $pc.
isuint64Value of register $is.

CALL: Call contract

DescriptionCall contract.
Operation
Syntaxcall $rA $rB $rC $rD
Encoding0x00 rA rB rC rD
EffectsExternal call
Notes

There is a balanceOfStart(asset_id: byte[32]) -> uint32 helper that returns the memory address of the remaining free balance of asset_id. If asset_id has no free balance remaining, the helper panics.

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rC + 32 overflows or > VM_MAX_RAM
  • Contract with ID MEM[$rA, 32] is not in tx.inputs
  • Reading past MEM[VM_MAX_RAM - 1]
  • In an external context, if $rB > MEM[balanceOfStart(MEM[$rC, 32]), 8]
  • In an internal context, if $rB is greater than the balance of asset ID MEM[$rC, 32] of output with contract ID MEM[$fp, 32]

Register $rA is a memory address from which the following fields are set (word-aligned):

bytestypevaluedescription
32byte[32]toContract ID to call.
8byte[8]param1First parameter.
8byte[8]param2Second parameter.

$rB is the amount of coins to forward. $rC points to the 32-byte asset ID of the coins to forward. $rD is the amount of gas to forward. If it is set to an amount greater than the available gas, all available gas is forwarded.

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.Call
frombyte[32]Contract ID of current context if in an internal context, zero otherwise.
tobyte[32]Contract ID of called contract.
amountuint64Amount of coins to forward, i.e. $rB.
asset_idbyte[32]Asset ID of coins to forward, i.e. MEM[$rC, 32].
gasuint64Gas to forward, i.e. min($rD, $cgas).
param1uint64First parameter.
param2uint64Second parameter.
pcuint64Value of register $pc.
isuint64Value of register $is.

For output with contract ID MEM[$rA, 32], increase balance of asset ID MEM[$rC, 32] by $rB. In an external context, decrease MEM[balanceOfStart(MEM[$rC, 32]), 8] by $rB. In an internal context, decrease asset ID MEM[$rC, 32] balance of output with contract ID MEM[$fp, 32] by $rB.

A call frame is pushed at $sp. In addition to filling in the values of the call frame, the following registers are set:

  1. $fp = $sp (on top of the previous call frame is the beginning of this call frame)
  2. Set $ssp and $sp to the start of the writable stack area of the call frame.
  3. Set $pc and $is to the starting address of the code.
  4. $flag set to zero.
  5. $bal = $rB (forward coins)
  6. $cgas = $rD or all available gas (forward gas)

This modifies the balanceRoot field of the appropriate output(s).

CB: Coinbase contract id

DescriptionGet the coinbase contract id associated with the block proposer.
OperationMEM[$rA, 32] = coinbase();
Syntaxcb $rA
Encoding0x00 rA - - -
Notes

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • The memory range MEM[$rA, 32] does not pass ownership check

CCP: Code copy

DescriptionCopy $rD bytes of code starting at $rC for contract with ID equal to the 32 bytes in memory starting at $rB into memory starting at $rA.
OperationMEM[$rA, $rD] = code($rB, $rC, $rD);
Syntaxccp $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
NotesIf $rD is greater than the code size, zero bytes are filled in.

This is used only for reading and inspecting code of other contracts. Use LDC to load code for executing.

Panic if:

  • $rA + $rD overflows or > VM_MAX_RAM
  • $rB + 32 overflows or > VM_MAX_RAM
  • The memory range MEM[$rA, $rD] does not pass ownership check
  • Contract with ID MEM[$rB, 32] is not in tx.inputs

CROO: Code Merkle root

DescriptionSet the 32 bytes in memory starting at $rA to the code root for contract with ID equal to the 32 bytes in memory starting at $rB.
OperationMEM[$rA, 32] = coderoot(MEM[$rB, 32]);
Syntaxcroo $rA, $rB
Encoding0x00 rA rB - -
Notes

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rB + 32 overflows or > VM_MAX_RAM
  • The memory range MEM[$rA, 32] does not pass ownership check
  • Contract with ID MEM[$rB, 32] is not in tx.inputs

Code root computation is defined here.

CSIZ: Code size

DescriptionSet $rA to the size of the code for contract with ID equal to the 32 bytes in memory starting at $rB.
Operation$rA = codesize(MEM[$rB, 32]);
Syntaxcsiz $rA, $rB
Encoding0x00 rA rB - -
Notes

Panic if:

  • $rA is a reserved register
  • $rB + 32 overflows or > VM_MAX_RAM
  • Contract with ID MEM[$rB, 32] is not in tx.inputs

LDC: Load code from an external contract, blob or memory

DescriptionCopy $rC bytes of code at offset $rB from object identified with $rA into memory starting at $ssp. Object type is in imm.
Operationcode = match imm { 0 => contract_code(mem[$rA,32]), 1 => blob_payload(mem[$rA,32]), 2 => mem[$ra, ..] }; MEM[$ssp, $rC] = code[$rB, $rC];
Syntaxldc $rA, $rB, $rC, imm
Encoding0x00 rA rB rC imm
NotesIf $rC is greater than the code size, zero bytes are filled in. Final length is always padded to word boundary.

Object type from imm determines the source for loading as follows:

immObject type
0Contract code
1Blob payload
2VM memory
otherreserved

Panic if:

  • $ssp + $rC overflows or > VM_MAX_RAM
  • imm <= 1 and $rA + 32 overflows or > VM_MAX_RAM
  • $ssp + $rC >= $hp
  • imm == 0 and $rC > CONTRACT_MAX_SIZE
  • imm == 0 and contract with ID MEM[$rA, 32] is not in tx.inputs
  • imm == 0 and context is a predicate
  • imm == 1 and blob with ID MEM[$rA, 32] is not found in the chain state
  • imm == 2 and $rA + $rB + $rC overflows or > VM_MAX_RAM
  • imm >= 3 (reserved value)

Increment $fp->codesize, $ssp by $rC padded to word alignment. Then set $sp to $ssp.

This instruction can be used to concatenate the code of multiple contracts or blobs together. It can only be used when the stack area of the call frame is zero-sized.

LOG: Log event

DescriptionLog an event. This is a no-op.
Operationlog($rA, $rB, $rC, $rD);
Syntaxlog $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
Notes

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.Log
idbyte[32]Contract ID of current context if in an internal context, zero otherwise.
val0uint64Value of register $rA.
val1uint64Value of register $rB.
val2uint64Value of register $rC.
val3uint64Value of register $rD.
pcuint64Value of register $pc.
isuint64Value of register $is.

LOGD: Log data event

DescriptionLog an event. This is a no-op.
Operationlogd($rA, $rB, $rC, $rD);
Syntaxlogd $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
Notes

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.LogData
idbyte[32]Contract ID of current context if in an internal context, zero otherwise.
val0uint64Value of register $rA.
val1uint64Value of register $rB.
ptruint64Value of register $rC.
lenuint64Value of register $rD.
digestbyte[32]Hash of MEM[$rC, $rD].
pcuint64Value of register $pc.
isuint64Value of register $is.

Logs the memory range MEM[$rC, $rD].

Panics if:

  • $rC + $rD overflows or > VM_MAX_RAM

MINT: Mint new coins

DescriptionMint $rA coins of the $rB ID from the current contract.
Operationmint($rA, $rB);
Syntaxmint $rA $rB
Encoding0x00 rA rB - -
Notes$rB is a pointer to a 32 byte ID in memory

The asset ID will be constructed using the asset ID construction method.

Panic if:

  • $rB + 32 overflows or > VM_MAX_RAM
  • Balance of asset ID constructAssetID(MEM[$fp, 32], MEM[$rB]) of output with contract ID MEM[$fp, 32] plus $rA overflows
  • $fp == 0 (in the script context)

For output with contract ID MEM[$fp, 32], increase balance of asset ID constructAssetID(MEM[$fp, 32], MEM[$rB]) by $rA.

This modifies the balanceRoot field of the appropriate output.

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.Mint
sub_idbyte[32]Asset sub identifier MEM[$rB, $rB + 32].
contract_idbyte[32]Contract ID of the current context.
valuint64Value of register $rA.
pcuint64Value of register $pc.
isuint64Value of register $is.

RETD: Return from context with data

DescriptionReturns from context with value MEM[$rA, $rB].
Operationreturndata($rA, $rB);
Syntaxretd $rA, $rB
Encoding0x00 rA rB - -
Notes

Panic if:

  • $rA + $rB overflows or > VM_MAX_RAM

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.ReturnData
idbyte[32]Contract ID of current context if in an internal context, zero otherwise.
ptruint64Value of register $rA.
lenuint64Value of register $rB.
digestbyte[32]Hash of MEM[$rA, $rB].
pcuint64Value of register $pc.
isuint64Value of register $is.

If current context is a script, append an additional receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.ScriptResult
resultuint640
gas_useduint64Gas consumed by the script.

If current context is external, cease VM execution and return MEM[$rA, $rB].

Returns from contract call, popping the call frame. Before popping, perform the following operations.

Return the unused forwarded gas to the caller:

  1. $cgas = $cgas + $fp->$cgas (add remaining context gas from previous context to current remaining context gas)

Set the return value:

  1. $ret = $rA
  2. $retl = $rB

Then pop the call frame and restore all registers except $ggas, $cgas, $ret, $retl and $hp. Afterwards, set the following registers:

  1. $pc = $pc + 4 (advance program counter from where we called)

RVRT: Revert

DescriptionHalt execution, reverting state changes and returning value in $rA.
Operationrevert($rA);
Syntaxrvrt $rA
Encoding0x00 rA - - -
Notes

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.Revert
idbyte[32]Contract ID of current context if in an internal context, zero otherwise.
valuint64Value of register $rA.
pcuint64Value of register $pc.
isuint64Value of register $is.

Then append an additional receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.ScriptResult
resultuint641
gas_useduint64Gas consumed by the script.

Cease VM execution and revert script effects. After a revert:

  1. All OutputContract outputs will have the same balanceRoot and stateRoot as on initialization.
  2. All OutputVariable outputs will have to, amount, and asset_id of zero.

SMO: Send message out

DescriptionSend a message to recipient address MEM[$rA, 32] from the MEM[$fp, 32] sender with message data MEM[$rB, $rC] and the $rD amount of base asset coins.
Operationoutputmessage(MEM[$fp, 32], MEM[$rA, 32], MEM[$rB, $rC], $rD);
Syntaxsmo $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
EffectsOutput message
Notes

There is a balanceOfStart(asset_id: byte[32]) -> uint32 helper that returns the memory address of the remaining free balance of asset_id. If asset_id has no free balance remaining, the helper panics.

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rB + $rC overflows or > VM_MAX_RAM
  • $rC > MESSAGE_MAX_DATA_SIZE
  • In an external context, if $rD > MEM[balanceOfStart(0), 8]
  • In an internal context, if $rD is greater than the balance of asset ID 0 of output with contract ID MEM[$fp, 32]

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.MessageOut
senderbyte[32]The address of the message sender: MEM[$fp, 32].
recipientbyte[32]The address of the message recipient: MEM[$rA, 32].
amountuint64Amount of base asset coins sent with message: $rD.
noncebyte[32]The message nonce as described here.
lenuint64Length of message data, in bytes: $rC.
digestbyte[32]Hash of MEM[$rB, $rC].

In an external context, decrease MEM[balanceOfStart(0), 8] by $rD. In an internal context, decrease asset ID 0 balance of output with contract ID MEM[$fp, 32] by $rD. This modifies the balanceRoot field of the appropriate contract that had its' funds deducted.

SCWQ: State clear sequential 32 byte slots

DescriptionA sequential series of 32 bytes is cleared from the current contract's state.
OperationSTATE[MEM[$rA, 32], 32 * $rC] = None;
Syntaxscwq $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rB is a reserved register
  • $fp == 0 (in the script context)

Register $rB will be set to false if any storage slot in the requested range was already unset (default) and true if all the slots were set.

SRW: State read word

DescriptionA word is read from the current contract's state.
Operation$rA = STATE[MEM[$rC, 32]][0, 8];
Syntaxsrw $rA, $rB, $rC
Encoding0x00 rA rB rC -
EffectsStorage read
NotesReturns zero if the state element does not exist.

Panic if:

Register $rB will be set to false if the requested slot is unset (default) and true if it's set.

SRWQ: State read sequential 32 byte slots

DescriptionA sequential series of 32 bytes is read from the current contract's state.
OperationMEM[$rA, 32 * rD] = STATE[MEM[$rC, 32], 32 * rD];
Syntaxsrwq $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
EffectsStorage read
NotesReturns zero if the state element does not exist.

Panic if:

  • $rA + 32 * rD overflows or > VM_MAX_RAM
  • $rC + 32 * rD overflows or > VM_MAX_RAM
  • $rB is a reserved register
  • The memory range MEM[$rA, 32 * rD] does not pass ownership check
  • $fp == 0 (in the script context)

Register $rB will be set to false if any storage slot in the requested range is unset (default) and true if all the slots are set.

SWW: State write word

DescriptionA word is written to the current contract's state.
OperationSTATE[MEM[$rA, 32]][0, 8] = $rC;
STATE[MEM[$rA, 32]][8, 24] = 0;
Syntaxsww $rA $rB $rC
Encoding0x00 rA rB rC -
EffectsStorage write
NotesAdditional gas is charged when a new storage slot is created.

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rB is a reserved register
  • $fp == 0 (in the script context)

The last 24 bytes of STATE[MEM[$rA, 32]] are set to 0. Register $rB will be set to the number of new slots written, i.e. 1 if the slot was previously unset, and 0 if it already contained a value.

SWWQ: State write sequential 32 byte slots

DescriptionA sequential series of 32 bytes is written to the current contract's state.
OperationSTATE[MEM[$rA, 32], 32 * $rD] = MEM[$rC, 32 * $rD];
Syntaxswwq $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
EffectsStorage write
NotesAdditional gas is charged when for each new storage slot created.

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rC + 32 * $rD overflows or > VM_MAX_RAM
  • $rB is a reserved register
  • $fp == 0 (in the script context)

Register $rB will be set to the number of storage slots that were previously unset, and were set by this operation.

TIME: Timestamp at height

DescriptionGet timestamp of block at given height.
Operation$rA = time($rB);
Syntaxtime $rA, $rB
Encoding0x00 rA rB - -
Notes

Panic if:

Gets the timestamp of the block at height $rB. Time is in TAI64 format.

TR: Transfer coins to contract

DescriptionTransfer $rB coins with asset ID at $rC to contract with ID at $rA.
Operationtransfer(MEM[$rA, 32], $rB, MEM[$rC, 32]);
Syntaxtr $rA, $rB, $rC
Encoding0x00 rA rB rC -
EffectsBalance tree read, balance tree write
Notes

There is a balanceOfStart(asset_id: byte[32]) -> uint32 helper that returns the memory address of the remaining free balance of asset_id. If asset_id has no free balance remaining, the helper panics.

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rC + 32 overflows or > VM_MAX_RAM
  • Contract with ID MEM[$rA, 32] is not in tx.inputs
  • In an external context, if $rB > MEM[balanceOfStart(MEM[$rC, 32]), 8]
  • In an internal context, if $rB is greater than the balance of asset ID MEM[$rC, 32] of output with contract ID MEM[$fp, 32]
  • $rB == 0

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.Transfer
frombyte[32]Contract ID of current context if in an internal context, zero otherwise.
tobyte[32]Contract ID of contract to transfer coins to.
amountuint64Amount of coins transferred.
asset_idbyte[32]asset ID of coins transferred.
pcuint64Value of register $pc.
isuint64Value of register $is.

For output with contract ID MEM[$rA, 32], increase balance of asset ID MEM[$rC, 32] by $rB. In an external context, decrease MEM[balanceOfStart(MEM[$rC, 32]), 8] by $rB. In an internal context, decrease asset ID MEM[$rC, 32] balance of output with contract ID MEM[$fp, 32] by $rB.

This modifies the balanceRoot field of the appropriate output(s).

TRO: Transfer coins to output

DescriptionTransfer $rC coins with asset ID at $rD to address at $rA, with output $rB.
Operationtransferout(MEM[$rA, 32], $rB, $rC, MEM[$rD, 32]);
Syntaxtro $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
EffectsBalance tree read, balance tree write
Notes

There is a balanceOfStart(asset_id: byte[32]) -> uint32 helper that returns the memory address of the remaining free balance of asset_id. If asset_id has no free balance remaining, the helper panics.

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rD + 32 overflows or > VM_MAX_RAM
  • $rB > tx.outputsCount
  • In an external context, if $rC > MEM[balanceOfStart(MEM[$rD, 32]), 8]
  • In an internal context, if $rC is greater than the balance of asset ID MEM[$rD, 32] of output with contract ID MEM[$fp, 32]
  • $rC == 0
  • tx.outputs[$rB].type != OutputType.Variable
  • tx.outputs[$rB].amount != 0

Append a receipt to the list of receipts:

nametypedescription
typeReceiptTypeReceiptType.TransferOut
frombyte[32]Contract ID of current context if in an internal context, zero otherwise.
tobyte[32]Address to transfer coins to.
amountuint64Amount of coins transferred.
asset_idbyte[32]asset ID of coins transferred.
pcuint64Value of register $pc.
isuint64Value of register $is.

In an external context, decrease MEM[balanceOfStart(MEM[$rD, 32]), 8] by $rC. In an internal context, decrease asset ID MEM[$rD, 32] balance of output with contract ID MEM[$fp, 32] by $rC. Then set:

  • tx.outputs[$rB].to = MEM[$rA, 32]
  • tx.outputs[$rB].amount = $rC
  • tx.outputs[$rB].asset_id = MEM[$rD, 32]

This modifies the balanceRoot field of the appropriate output(s).

Blob Instructions

All these instructions advance the program counter $pc by 4 after performing their operation.

BSIZ: Blob size

DescriptionSet $rA to the size of the blob with ID equal to the 32 bytes in memory starting at $rB.
Operation$rA = len(blob(MEM[$rB, 32]));
Syntaxbsiz $rA, $rB
Encoding0x00 rA rB - -
Notes

Panic if:

  • $rA is a reserved register
  • $rB + 32 overflows or > VM_MAX_RAM
  • Blob ID MEM[$rB, 32] is not found

BLDD: Load data from a blob

|-------------|-------------------------------------------------------------------------------------------------------------| | Description | Load 32-byte blob id at $rB, and copy $rD bytes starting from $rC into $sA. | | Operation | MEM[$rA, $rD] = blob(MEM[$rB, 32])[$rC, $rD]; | | Syntax | bldd $rA, $rB, rC, $rD | | Encoding | 0x00 rA rB rC rD | | Notes | If $rC > blob size, zero bytes are filled in. |

Panic if:

  • $rA + $rD overflows or > VM_MAX_RAM or > $hp
  • $rB + 32 overflows or > VM_MAX_RAM
  • Blob ID MEM[$rB, 32] is not found

Cryptographic Instructions

All these instructions advance the program counter $pc by 4 after performing their operation.

ECK1: Secp256k1 signature recovery

DescriptionThe 64-byte public key (x, y) recovered from 64-byte signature starting at $rB on 32-byte message hash starting at $rC.
OperationMEM[$rA, 64] = ecrecover_k1(MEM[$rB, 64], MEM[$rC, 32]);
Syntaxeck1 $rA, $rB, $rC
Encoding0x00 rA rB rC -
NotesTakes message hash as an input. You can use S256 to hash the message if needed.

Panic if:

  • $rA + 64 overflows or > VM_MAX_RAM
  • $rB + 64 overflows or > VM_MAX_RAM
  • $rC + 32 overflows or > VM_MAX_RAM
  • The memory range MEM[$rA, 64] does not pass ownership check

Signatures and signature verification are specified here.

If the signature cannot be verified, MEM[$rA, 64] is set to 0 and $err is set to 1, otherwise $err is cleared.

To get the address from the public key, hash the public key with SHA-2-256.

ECR1: Secp256r1 signature recovery

DescriptionThe 64-byte public key (x, y) recovered from 64-byte signature starting at $rB on 32-byte message hash starting at $rC.
OperationMEM[$rA, 64] = ecrecover_r1(MEM[$rB, 64], MEM[$rC, 32]);
Syntaxecr1 $rA, $rB, $rC
Encoding0x00 rA rB rC -
NotesTakes message hash as an input. You can use S256 to hash the message if needed.

Panic if:

  • $rA + 64 overflows or > VM_MAX_RAM
  • $rB + 64 overflows or > VM_MAX_RAM
  • $rC + 32 overflows or > VM_MAX_RAM
  • The memory range MEM[$rA, 64] does not pass ownership check

Signatures and signature verification are specified here.

If the signature cannot be verified, MEM[$rA, 64] is set to 0 and $err is set to 1, otherwise $err is cleared.

To get the address from the public key, hash the public key with SHA-2-256.

ED19: EdDSA curve25519 verification

DescriptionVerification 64-byte signature at $rB with 32-byte public key at $rA for a message starting at $rC with length $rD.
Operationed19verify(MEM[$rA, 32], MEM[$rB, 64], MEM[$rC, $rD]);
Syntaxed19 $rA, $rB, $rC, $rD
Encoding0x00 rA rB rC rD
NotesTakes message instead of hash. For backwards compatibility reasons, if $rD == 0, it will be treated as 32.

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rB + 64 overflows or > VM_MAX_RAM
  • $rC + $rD overflows or > VM_MAX_RAM

Verification are specified here.

If there is an error in verification, $err is set to 1, otherwise $err is cleared.

K256: keccak-256

DescriptionThe keccak-256 hash of $rC bytes starting at $rB.
OperationMEM[$rA, 32] = keccak256(MEM[$rB, $rC]);
Syntaxk256 $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rB + $rC overflows or > VM_MAX_RAM
  • The memory range MEM[$rA, 32] does not pass ownership check

S256: SHA-2-256

DescriptionThe SHA-2-256 hash of $rC bytes starting at $rB.
OperationMEM[$rA, 32] = sha256(MEM[$rB, $rC]);
Syntaxs256 $rA, $rB, $rC
Encoding0x00 rA rB rC -
Notes

Panic if:

  • $rA + 32 overflows or > VM_MAX_RAM
  • $rB + $rC overflows or > VM_MAX_RAM
  • The memory range MEM[$rA, 32] does not pass ownership check

Other Instructions

All these instructions advance the program counter $pc by 4 after performing their operation.

ECAL: Call external function

DescriptionCall an external function that has full access to the VM state.
Operationexternal(&mut vm, $rA, $rB, $rC, $rD)
Syntaxecal $rA $rB $rC $rD
Encoding0x00 rA rB rC rD
NotesDoes nothing by default, but the VM user can define this to do anything.

This function provides an escape hatch from the VM, similar to ecall instruction of RISC-V. The suggested convention is to use $rA for "system call number", i.e. identifying the procedure to call, but all arguments can be used freely. The operation can modify the VM state freely, including writing to registers and memory. Again, the suggested convention is to use $rA for the return value and $err for any possible errors. However, these conventions can be ignored when necessary.

Panic if:

  • The external function panics.

FLAG: Set flags

DescriptionSet $flag to $rA.
Operation$flag = $rA;
Syntaxflag $rA
Encoding0x00 rA - - -
Notes

Panic if:

  • Any reserved flags are set

GM: Get metadata

DescriptionGet metadata from memory.
OperationVaries (see below).
Syntaxgm $rA, imm
Encoding0x00 rA imm imm imm
Notes

Read metadata from memory. A convenience instruction to avoid manually extracting metadata.

namevaluedescription
GM_IS_CALLER_EXTERNAL0x00001Get if caller is external.
GM_GET_CALLER0x00002Get caller's contract ID.
GM_GET_VERIFYING_PREDICATE0x00003Get index of current predicate.
GM_GET_CHAIN_ID0x00004Get the value of CHAIN_ID
GM_TX_START0x00005Transaction start memory address
GM_BASE_ASSET_ID0x00006Base asset ID

If imm == GM_IS_CALLER_EXTERNAL:

Panic if:

  • $fp == 0 (in an external context)

Set $rA to true if parent is an external context, false otherwise.

If imm == GM_GET_CALLER:

Panic if:

  • $fp == 0 (in an external context)
  • $fp->$fp == 0 (if parent context is external)

Set $rA to $fp->$fp (i.e. $rA will point to the previous call frame's contract ID).

If imm == GM_GET_VERIFYING_PREDICATE:

Panic if:

  • not in a predicate context

Set $rA to the index of the currently-verifying predicate.

GTF: Get transaction fields

DescriptionGet transaction fields.
OperationVaries (see below).
Syntaxgtf $rA, $rB, imm
Encoding0x00 rA rB i i
Notes

Get fields from the transaction.

nameimmset $rA to
GTF_TYPE0x001tx.type
GTF_SCRIPT_GAS_LIMIT0x002tx.scriptGasLimit
GTF_SCRIPT_SCRIPT_LENGTH0x003tx.scriptLength
GTF_SCRIPT_SCRIPT_DATA_LENGTH0x004tx.scriptDataLength
GTF_SCRIPT_INPUTS_COUNT0x005tx.inputsCount
GTF_SCRIPT_OUTPUTS_COUNT0x006tx.outputsCount
GTF_SCRIPT_WITNESSES_COUNT0x007tx.witnessesCount
GTF_SCRIPT_SCRIPT0x009Memory address of tx.script
GTF_SCRIPT_SCRIPT_DATA0x00AMemory address of tx.scriptData
GTF_SCRIPT_INPUT_AT_INDEX0x00BMemory address of tx.inputs[$rB]
GTF_SCRIPT_OUTPUT_AT_INDEX0x00CMemory address of t.outputs[$rB]
GTF_SCRIPT_WITNESS_AT_INDEX0x00DMemory address of tx.witnesses[$rB]
GTF_TX_LENGTH0x00ELength of raw transaction types in memory
GTF_CREATE_BYTECODE_WITNESS_INDEX0x101tx.bytecodeWitnessIndex
GTF_CREATE_STORAGE_SLOTS_COUNT0x102tx.storageSlotsCount
GTF_CREATE_INPUTS_COUNT0x103tx.inputsCount
GTF_CREATE_OUTPUTS_COUNT0x104tx.outputsCount
GTF_CREATE_WITNESSES_COUNT0x105tx.witnessesCount
GTF_CREATE_SALT0x106Memory address of tx.salt
GTF_CREATE_STORAGE_SLOT_AT_INDEX0x107Memory address of tx.storageSlots[$rB]
GTF_CREATE_INPUT_AT_INDEX0x108Memory address of tx.inputs[$rB]
GTF_CREATE_OUTPUT_AT_INDEX0x109Memory address of t.outputs[$rB]
GTF_CREATE_WITNESS_AT_INDEX0x10AMemory address of tx.witnesses[$rB]
GTF_INPUT_TYPE0x200tx.inputs[$rB].type
GTF_INPUT_COIN_TX_ID0x201Memory address of tx.inputs[$rB].txID
GTF_INPUT_COIN_OUTPUT_INDEX0x202tx.inputs[$rB].outputIndex
GTF_INPUT_COIN_OWNER0x203Memory address of tx.inputs[$rB].owner
GTF_INPUT_COIN_AMOUNT0x204tx.inputs[$rB].amount
GTF_INPUT_COIN_ASSET_ID0x205Memory address of tx.inputs[$rB].asset_id
GTF_INPUT_COIN_WITNESS_INDEX0x207tx.inputs[$rB].witnessIndex
GTF_INPUT_COIN_PREDICATE_LENGTH0x209tx.inputs[$rB].predicateLength
GTF_INPUT_COIN_PREDICATE_DATA_LENGTH0x20Atx.inputs[$rB].predicateDataLength
GTF_INPUT_COIN_PREDICATE0x20BMemory address of tx.inputs[$rB].predicate
GTF_INPUT_COIN_PREDICATE_DATA0x20CMemory address of tx.inputs[$rB].predicateData
GTF_INPUT_COIN_PREDICATE_GAS_USED0x20Dtx.inputs[$rB].predicateGasUsed
GTF_INPUT_CONTRACT_CONTRACT_ID0x225Memory address of tx.inputs[$rB].contractID
GTF_INPUT_MESSAGE_SENDER0x240Memory address of tx.inputs[$rB].sender
GTF_INPUT_MESSAGE_RECIPIENT0x241Memory address of tx.inputs[$rB].recipient
GTF_INPUT_MESSAGE_AMOUNT0x242tx.inputs[$rB].amount
GTF_INPUT_MESSAGE_NONCE0x243Memory address of tx.inputs[$rB].nonce
GTF_INPUT_MESSAGE_WITNESS_INDEX0x244tx.inputs[$rB].witnessIndex
GTF_INPUT_MESSAGE_DATA_LENGTH0x245tx.inputs[$rB].dataLength
GTF_INPUT_MESSAGE_PREDICATE_LENGTH0x246tx.inputs[$rB].predicateLength
GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH0x247tx.inputs[$rB].predicateDataLength
GTF_INPUT_MESSAGE_DATA0x248Memory address of tx.inputs[$rB].data
GTF_INPUT_MESSAGE_PREDICATE0x249Memory address of tx.inputs[$rB].predicate
GTF_INPUT_MESSAGE_PREDICATE_DATA0x24AMemory address of tx.inputs[$rB].predicateData
GTF_INPUT_MESSAGE_PREDICATE_GAS_USED0x24Btx.inputs[$rB].predicateGasUsed
GTF_OUTPUT_TYPE0x300tx.outputs[$rB].type
GTF_OUTPUT_COIN_TO0x301Memory address of tx.outputs[$rB].to
GTF_OUTPUT_COIN_AMOUNT0x302tx.outputs[$rB].amount
GTF_OUTPUT_COIN_ASSET_ID0x303Memory address of tx.outputs[$rB].asset_id
GTF_OUTPUT_CONTRACT_INPUT_INDEX0x304tx.outputs[$rB].inputIndex
GTF_OUTPUT_CONTRACT_BALANCE_ROOT0x305Memory address of tx.outputs[$rB].balanceRoot
GTF_OUTPUT_CONTRACT_STATE_ROOT0x306Memory address of tx.outputs[$rB].stateRoot
GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID0x307Memory address of tx.outputs[$rB].contractID
GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT0x308Memory address of tx.outputs[$rB].stateRoot
GTF_WITNESS_DATA_LENGTH0x400tx.witnesses[$rB].dataLength
GTF_WITNESS_DATA0x401Memory address of tx.witnesses[$rB].data
GTF_POLICY_TYPES0x500tx.policies.policyTypes
GTF_POLICY_TIP0x501tx.policies[0x00].tip
GTF_POLICY_WITNESS_LIMIT0x502tx.policies[count_ones(0b11 & tx.policyTypes) - 1].witnessLimit
GTF_POLICY_MATURITY0x503tx.policies[count_ones(0b111 & tx.policyTypes) - 1].maturity
GTF_POLICY_MAX_FEE0x504tx.policies[count_ones(0b1111 & tx.policyTypes) - 1].maxFee

Panic if:

  • $rA is a reserved register
  • imm is not one of the values listed above
  • The value of $rB results in an out of bounds access for variable-length fields
  • The input or output type does not match (OutputChange and OutputVariable count as OutputCoin)
  • The requested policy type is not set for this transaction.

For fixed-length fields, the value of $rB is ignored.

Networks

Specifications for network-specific components of the protocol.

PoA Network

Consensus Header

Wraps the application header.

nametypedescription
prevRootbyte[32]Merkle root of all previous consensus header hashes (i.e. not including this block).
heightuint32Height of this block.
timestampuint64Time this block was created, in TAI64 format.
applicationHashbyte[32]Hash of serialized application header for this block.

Consensus for the consensus header is a single signature from the authority over the hash of the serialized consensus header.

Since the system is secure under the assumption the authority is honest, there is no need for committing to the authority signatures in the header.

Testing

Test suites for verifying the correctness of a Fuel implementation.

Sparse Merkle Tree Test Specifications

Version

0.1.1

Last updated 2022/07/11

Abstract

This document outlines a test suite specification that can be used to verify the correctness of a Sparse Merkle Tree's outputs. The scope of this document covers only Sparse Merkle Tree (SMT) implementations that are compliant with Celestia Sparse Merkle Tree Specification. The goal of this document is to equip SMT library developers with a supplemental indicator of correctness. Libraries implementing an SMT can additionally implement this test suite specification in the code base's native language. Passing all tests in the concrete test suite is an indication of correctness and consistency with the reference specification; however, it is not an absolute guarantee.

The tests described in this document are designed to test features common to most Sparse Merkle Tree implementations. Test specifications are agnostic of the implementation details or language, and therefore take a black-box testing approach. A test specification may provide an example of what a compliant test may look like in the form of pseudocode.

A test specification follows the format:

  • Test name
  • Test description
  • Test inputs
  • Test outputs
  • Example pseudocode

For a concrete test to comply with its corresponding test specification, the System Under Test (SUT) must take in the prescribed inputs. When the SUT produces the prescribed outputs, the test passes. When the SUT produces any result or error that is not prescribed by the specification, the test fails. For a library to comply with the complete specification described herein, it must implement all test specifications, and each test must pass.

All test specifications assume that the Merkle Tree implementation under test uses the SHA-2-256 hashing algorithm as defined in FIPS PUB 180-4 to produce its outputs. The following test cases stipulate a theoretical function Sum(N) that takes in a big endian data slice N and returns the 32 byte SHA-256 hash of N.

Root Signature Tests

  1. Test Empty Root
  2. Test Update 1
  3. Test Update 2
  4. Test Update 3
  5. Test Update 5
  6. Test Update 10
  7. Test Update 100
  8. Test Update With Repeated Inputs
  9. Test Update Overwrite Key
  10. Test Update Union
  11. Test Update Sparse Union
  12. Test Update With Empty Data
  13. Test Update With Empty Data Performs Delete
  14. Test Update 1 Delete 1
  15. Test Update 2 Delete 1
  16. Test Update 10 Delete 5
  17. Test Delete Non-existent Key
  18. Test Interleaved Update Delete
  19. Test Delete Sparse Union

Test Empty Root

Description:

Tests the default root given no update or delete operations. The input set is described by S = {Ø}.

Inputs:

No inputs.

Outputs:

  • The expected root signature: 0x0000000000000000000000000000000000000000000000000000000000000000

Example pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
root = smt.root()
expected_root = '0000000000000000000000000000000000000000000000000000000000000000'
expect(hex_encode(root), expected_root).to_be_equal

Test Update 1

Description:

Tests the root after performing a single update call with the specified input.

Inputs:

  1. Update the empty tree with (K, D) where leaf key K = Sum(0u32) (32 bytes) and leaf data D = b"DATA" (bytes, UTF-8)

Outputs:

  • The expected root signature: 0x39f36a7cb4dfb1b46f03d044265df6a491dffc1034121bc1071a34ddce9bb14b

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
smt.update(&sum(b"\x00\x00\x00\x00"), b"DATA")
root = smt.root()
expected_root = '39f36a7cb4dfb1b46f03d044265df6a491dffc1034121bc1071a34ddce9bb14b'
expect(hex_encode(root), expected_root).to_be_equal

Test Update 2

Description:

Tests the root after performing two update calls with the specified inputs.

Inputs:

  1. Update the empty tree with (K, D), where leaf key K = Sum(0u32) and leaf data D = b"DATA" (bytes, UTF-8)
  2. Update the tree with (K, D), where leaf key K = Sum(1u32) and leaf data D = b"DATA" (bytes, UTF-8)

Outputs:

  • The expected root signature: 0x8d0ae412ca9ca0afcb3217af8bcd5a673e798bd6fd1dfacad17711e883f494cb

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
smt.update(&sum(b"\x00\x00\x00\x00"), b"DATA")
smt.update(&sum(b"\x00\x00\x00\x01"), b"DATA")
root = smt.root()
expected_root = '8d0ae412ca9ca0afcb3217af8bcd5a673e798bd6fd1dfacad17711e883f494cb'
expect(hex_encode(root), expected_root).to_be_equal

Test Update 3

Description:

Tests the root after performing three update calls with the specified inputs.

Inputs:

  1. Update the empty tree with (K, D), where leaf key K = Sum(0u32) and leaf data D = b"DATA" (bytes, UTF-8)
  2. Update the tree with (K, D), where leaf key K = Sum(1u32) and leaf data D = b"DATA" (bytes, UTF-8)
  3. Update the tree with (K, D), where leaf key K = Sum(2u32) and leaf data D = b"DATA" (bytes, UTF-8)

Outputs:

  • The expected root signature: 0x52295e42d8de2505fdc0cc825ff9fead419cbcf540d8b30c7c4b9c9b94c268b7

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
smt.update(&sum(b"\x00\x00\x00\x00"), b"DATA")
smt.update(&sum(b"\x00\x00\x00\x01"), b"DATA")
smt.update(&sum(b"\x00\x00\x00\x02"), b"DATA")
root = smt.root()
expected_root = '52295e42d8de2505fdc0cc825ff9fead419cbcf540d8b30c7c4b9c9b94c268b7'
expect(hex_encode(root), expected_root).to_be_equal

Test Update 5

Description:

Tests the root after performing five update calls with the specified inputs.

Inputs:

  1. Update the empty tree with (K, D), where leaf key K = Sum(0u32) and leaf data D = b"DATA" (bytes, UTF-8)
  2. Update the tree with (K, D), where leaf key K = Sum(1u32) and leaf data D = b"DATA" (bytes, UTF-8)
  3. Update the tree with (K, D), where leaf key K = Sum(2u32) and leaf data D = b"DATA" (bytes, UTF-8)
  4. Update the tree with (K, D), where leaf key K = Sum(3u32) and leaf data D = b"DATA" (bytes, UTF-8)
  5. Update the tree with (K, D), where leaf key K = Sum(4u32) and leaf data D = b"DATA" (bytes, UTF-8)

Outputs:

  • The expected root signature: 0x108f731f2414e33ae57e584dc26bd276db07874436b2264ca6e520c658185c6b

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
for i in 0..5 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
root = smt.root()
expected_root = '108f731f2414e33ae57e584dc26bd276db07874436b2264ca6e520c658185c6b'
expect(hex_encode(root), expected_root).to_be_equal

Test Update 10

Description:

Tests the root after performing 10 update calls with the specified inputs.

Inputs:

  1. For each i in 0..10, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)

Outputs:

  • The expected root signature: 0x21ca4917e99da99a61de93deaf88c400d4c082991cb95779e444d43dd13e8849

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
for i in 0..10 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
root = smt.root()
expected_root = '21ca4917e99da99a61de93deaf88c400d4c082991cb95779e444d43dd13e8849'
expect(hex_encode(root), expected_root).to_be_equal

Test Update 100

Description:

Tests the root after performing 100 update calls with the specified inputs.

Inputs:

  1. For each i in 0..100, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)

Outputs:

  • The expected root signature: 0x82bf747d455a55e2f7044a03536fc43f1f55d43b855e72c0110c986707a23e4d

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
for i in 0..100 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
root = smt.root()
expected_root = '82bf747d455a55e2f7044a03536fc43f1f55d43b855e72c0110c986707a23e4d'
expect(hex_encode(root), expected_root).to_be_equal

Test Update With Repeated Inputs

Description:

Tests the root after performing two update calls with the same inputs. The resulting input set is described by S = {A} U {A} = {A}, where {A} is the input. This test expects a root signature identical to that produced by Test Update 1.

Inputs:

  1. Update the empty tree with (K, D), where leaf key K = Sum(0u32) and leaf data D = b"DATA" (bytes, UTF-8)
  2. Update the tree again with (K, D), where leaf key K = Sum(0u32) and leaf data D = b"DATA" (bytes, UTF-8)

Outputs:

  • The expected root signature: 0x39f36a7cb4dfb1b46f03d044265df6a491dffc1034121bc1071a34ddce9bb14b

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
smt.update(&sum(b"\x00\x00\x00\x00"), b"DATA")
smt.update(&sum(b"\x00\x00\x00\x00"), b"DATA")
root = smt.root()
expected_root = '39f36a7cb4dfb1b46f03d044265df6a491dffc1034121bc1071a34ddce9bb14b'
expect(hex_encode(root), expected_root).to_be_equal

Test Update Overwrite Key

Description:

Tests the root after performing two update calls with the same leaf keys but different leaf data. The second update call is expected to overwrite the data originally written by the first update call.

Inputs:

  1. Update the empty tree with (K, D), where leaf key K = Sum(0u32) and leaf data D = b"DATA" (bytes, UTF-8)
  2. Update the tree with (K, D), where leaf key K = Sum(0u32) and leaf data D = b"CHANGE" (bytes, UTF-8)

Outputs:

  • The expected root signature: 0xdd97174c80e5e5aa3a31c61b05e279c1495c8a07b2a08bca5dbc9fb9774f9457

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
smt.update(&sum(b"\x00\x00\x00\x00"), b"DATA")
smt.update(&sum(b"\x00\x00\x00\x00"), b"CHANGE")
root = smt.root()
expected_root = 'dd97174c80e5e5aa3a31c61b05e279c1495c8a07b2a08bca5dbc9fb9774f9457'
expect(hex_encode(root), expected_root).to_be_equal

Test Update Union

Description:

Tests the root after performing update calls with discontinuous sets of inputs. The resulting input set is described by S = [0..5) U [10..15) U [20..25).

Inputs:

  1. For each i in 0..5, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)
  2. For each i in 10..15, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)
  3. For each i in 20..25, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)

Outputs:

  • The expected root signature: 0x7e6643325042cfe0fc76626c043b97062af51c7e9fc56665f12b479034bce326

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
for i in 0..5 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
for i in 10..15 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
for i in 20..25 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
root = smt.root()
expected_root = '7e6643325042cfe0fc76626c043b97062af51c7e9fc56665f12b479034bce326'
expect(hex_encode(root), expected_root).to_be_equal

Test Update Sparse Union

Description:

Tests the root after performing update calls with discontinuous sets of inputs. The resulting input set is described by S = [0, 2, 4, 6, 8].

Inputs:

  1. For each i in 0..5, update the tree with (K, D), where leaf key K = Sum(i * 2) and leaf data D = b"DATA" (bytes, UTF-8)

Outputs:

  • The expected root signature: 0xe912e97abc67707b2e6027338292943b53d01a7fbd7b244674128c7e468dd696

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
for i in 0..5 {
    key = &(i as u32 * 2).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
root = smt.root()
expected_root = 'e912e97abc67707b2e6027338292943b53d01a7fbd7b244674128c7e468dd696'
expect(hex_encode(root), expected_root).to_be_equal

Test Update With Empty Data

Description:

Tests the root after performing one update call with empty data. Updating the empty tree with empty data does not change the root, and the expected root remains the default root. The resulting input set is described by S = {Ø} U {Ø} = {Ø}. This test expects a root signature identical to that produced by Test Empty Root.

Inputs:

  1. Update the empty tree with (K, D), where leaf key K = Sum(0u32) and empty leaf data D = b"" (0 bytes)

Outputs:

  • The expected root signature: 0x0000000000000000000000000000000000000000000000000000000000000000

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
smt.update(&sum(b"\x00\x00\x00\x00"), b"")
root = smt.root()
expected_root = '0000000000000000000000000000000000000000000000000000000000000000'
expect(hex_encode(root), expected_root).to_be_equal

Test Update With Empty Data Performs Delete

Description:

Tests the root after performing one update call with arbitrary data followed by a second update call on the same key with empty data. Updating a key with empty data is equivalent to calling delete. By deleting the only key, we have an empty tree and expect to arrive at the default root. The resulting input set is described by S = {0} - {0} = {Ø}. This test expects a root signature identical to that produced by Test Empty Root.

Inputs:

  1. Update the empty tree with (K, D), where leaf key K = Sum(0u32) and leaf data D = b"DATA" (bytes, UTF-8)
  2. Update the tree with (K, D), where leaf key K = Sum(0u32) and empty leaf data D = b"" (0 bytes)

Outputs:

  • The expected root signature: 0x0000000000000000000000000000000000000000000000000000000000000000

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
smt.update(&sum(b"\x00\x00\x00\x00"), b"DATA")
smt.update(&sum(b"\x00\x00\x00\x00"), b"")
root = smt.root()
expected_root = '0000000000000000000000000000000000000000000000000000000000000000'
expect(hex_encode(root), expected_root).to_be_equal

Test Update 1 Delete 1

Description:

Tests the root after performing one update call followed by a subsequent delete call on the same key. By deleting the only key, we have an empty tree and expect to arrive at the default root. The resulting input set is described by S = {0} - {0} = {Ø}. This test expects a root signature identical to that produced by Test Empty Root.

Inputs:

  1. Update the empty tree with (K, D), where leaf key K = Sum(0u32) and leaf data D = b"DATA" (bytes, UTF-8)
  2. Delete (K) from the tree, where leaf key K = Sum(0u32)

Outputs:

  • The expected root signature: 0x0000000000000000000000000000000000000000000000000000000000000000

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
smt.update(&sum(b"\x00\x00\x00\x00"), b"DATA")
smt.delete(&sum(b"\x00\x00\x00\x00"))
root = smt.root()
expected_root = '0000000000000000000000000000000000000000000000000000000000000000'
expect(hex_encode(root), expected_root).to_be_equal

Test Update 2 Delete 1

Description:

Tests the root after performing two update calls followed by a subsequent delete call on the first key. By deleting the second key, we have a tree with only one key remaining, equivalent to a single update. This test expects a root signature identical to that produced by Test Update 1.

Inputs:

  1. Update the empty tree with (K, D), where leaf key K = Sum(0u32) and leaf data D = b"DATA" (bytes, UTF-8)
  2. Update the tree with (K, D), where leaf key K = Sum(1u32) and leaf data D = b"DATA" (bytes, UTF-8)
  3. Delete (K) from the tree, where leaf key K = Sum(1u32)

Outputs:

  • The expected root signature: 0x39f36a7cb4dfb1b46f03d044265df6a491dffc1034121bc1071a34ddce9bb14b

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
smt.update(&sum(b"\x00\x00\x00\x00"), b"DATA")
smt.update(&sum(b"\x00\x00\x00\x01"), b"DATA")
smt.delete(&sum(b"\x00\x00\x00\x01"))
root = smt.root()
expected_root = '39f36a7cb4dfb1b46f03d044265df6a491dffc1034121bc1071a34ddce9bb14b'
expect(hex_encode(root), expected_root).to_be_equal

Test Update 10 Delete 5

Description:

Tests the root after performing 10 update calls followed by 5 subsequent delete calls on the latter keys. By deleting the last five keys, we have a tree with the first five keys remaining, equivalent to five updates. This test expects a root signature identical to that produced by Test Update 5.

Inputs:

  1. For each i in 0..10, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)
  2. For each i in 5..10, delete (K) from the tree, where leaf key K = Sum(i)

Outputs:

  • The expected root signature: 0x108f731f2414e33ae57e584dc26bd276db07874436b2264ca6e520c658185c6b

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
for i in 0..10 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
for i in 5..10 {
    key = &(i as u32).to_big_endian_bytes()
    smt.delete(&sum(key))
}
root = smt.root()
expected_root = '108f731f2414e33ae57e584dc26bd276db07874436b2264ca6e520c658185c6b'
expect(hex_encode(root), expected_root).to_be_equal

Test Delete Non-existent Key

Description:

Tests the root after performing five update calls followed by a subsequent delete on a key that is not present in the input set. This test expects a root signature identical to that produced by Test Update 5.

Inputs:

  1. For each i in 0..5, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)
  2. Delete (K) from the tree, where leaf key K = Sum(1024u32)

Outputs:

  • The expected root signature: 0x108f731f2414e33ae57e584dc26bd276db07874436b2264ca6e520c658185c6b

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
for i in 0..5 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
smt.delete(&sum(b"\x00\x00\x04\x00"))

root = smt.root()
expected_root = '108f731f2414e33ae57e584dc26bd276db07874436b2264ca6e520c658185c6b'
expect(hex_encode(root), expected_root).to_be_equal

Test Interleaved Update Delete

Description:

Tests the root after performing a series of interleaved update and delete calls. The resulting input set is described by [0..5) U [10..15) U [20..25). This test demonstrates the inverse relationship between operations update and delete. This test expects a root signature identical to that produced by Test Update Union.

Inputs:

  1. For each i in 0..10, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)
  2. For each i in 5..15, delete (K) from the tree, where leaf key K = Sum(i) from the tree
  3. For each i in 10..20, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)
  4. For each i in 15..25, delete (K) from the tree, where leaf key K = Sum(i) from the tree
  5. For each i in 20..30, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)
  6. For each i in 25..35, delete (K) from the tree, where leaf key K = Sum(i) from the tree

Outputs:

  • The expected root signature: 0x7e6643325042cfe0fc76626c043b97062af51c7e9fc56665f12b479034bce326

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
for i in 0..10 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
for i in 5..15 {
    key = &(i as u32).to_big_endian_bytes()
    smt.delete(&sum(key))
}
for i in 10..20 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
for i in 15..25 {
    key = &(i as u32).to_big_endian_bytes()
    smt.delete(&sum(key))
}
for i in 20..30 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
for i in 25..35 {
    key = &(i as u32).to_big_endian_bytes()
    smt.delete(&sum(key))
}
root = smt.root()
expected_root = '7e6643325042cfe0fc76626c043b97062af51c7e9fc56665f12b479034bce326'
expect(hex_encode(root), expected_root).to_be_equal

Test Delete Sparse Union

Description:

Tests the root after performing delete calls with discontinuous sets of inputs. The resulting input set is described by S = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - [1, 3, 5, 7, 9] = [0, 2, 4, 6, 8]. This test expects a root signature identical to that produced by Test Update Sparse Union.

Inputs:

  1. For each i in 0..10, update the tree with (K, D), where leaf key K = Sum(i) and leaf data D = b"DATA" (bytes, UTF-8)
  2. For each i in 0..5, delete (K) from the tree, where leaf key K = Sum(i * 2 + 1)

Outputs:

  • The expected root signature: 0xe912e97abc67707b2e6027338292943b53d01a7fbd7b244674128c7e468dd696

Example Pseudocode:

smt = SparseMerkleTree.new(Storage.new(), sha256.new())
for i in 0..10 {
    key = &(i as u32).to_big_endian_bytes()
    data = b"DATA"
    smt.update(&sum(key), data)
}
for i in 0..5 {
    key = &(i as u32 * 2 + 1).to_big_endian_bytes()
    smt.delete(&sum(key))
}
root = smt.root()
expected_root = 'e912e97abc67707b2e6027338292943b53d01a7fbd7b244674128c7e468dd696'
expect(hex_encode(root), expected_root).to_be_equal

Defining the ABI

Next, we will define our ABI. ABI stands for Application Binary Interface. In a Sway contract, it serves as an outline of all the functions within the contract. For each function, you need to specify its name, input types, return types, level of storage access, and if it's payable.

The ABI for our contract is structured as follows. Write the ABI provided below into your main.sw file:

<TestAction id="sway-abi" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/src/main.sw' }} />

Don't be worried about understanding the specifics of each function at this moment. We will dive into detailed explanations in the "Functions" section.

Functions Structure

A function is defined using the fn keyword. In Sway, snake case is the convention, so instead of naming a function myFunction, you would name it my_function.

If the function returns a value, its return type must be defined using a skinny arrow. Additionally, if the function has parameters, their types must also be specified. Semicolons are required at the end of each statement.

If a function either reads from or writes to storage, you need to specify the access level above the function using annotations like #[storage(read)] or #[storage(read, write)].

For functions that are expected to receive funds when called, such as the buy_item function, the #[payable] annotation is required.

Building a Frontend to Interact With Your Contract

To build a frontend application for the counter contract, we'll do the following:

  1. Install the Fuel Browser Wallet.
  2. Initialize a React project.
  3. Install the fuels SDK dependency.
  4. Generate contract types.
  5. Write our frontend code.
  6. Run our project.

Install the Fuel Browser Wallet

{/install_wallet:example:start/} Our frontend application will allow users to connect with a wallet, so you'll need to have a browser wallet installed.

Before going to the next steps, install the Fuel Wallet extension.

Once you've setup your wallet, click the "Faucet" button in the wallet to get some testnet tokens. {/install_wallet:example:end/}

Initialize a React project

To split our project's contract from frontend code, let's initialize our frontend project: assuming that your terminal is open at your contract's folder /home/user/path/to/counter-contract let's go back up one directory.

cd ..

Now, initialize a React project with TypeScript using Vite.

<TestAction id="create-vite-project" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project' }} />

npm create vite@latest frontend -- --template react-ts

The output should be similar to this:

Scaffolding project in Fuel/fuel-project/frontend...

Done. Now run:

  cd frontend
  npm install
  npm run dev

Installing

Move into the frontend folder and install the dependencies by running:

<TestAction id="install-basic-deps" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project' }} />

cd frontend && npm install

You should now have two folders inside your fuel-project folder: counter-contract and frontend.

<Box.Centered> project folder structure </Box.Centered>

Install the fuels SDK dependency

The fuels package includes all the main tools you need to interact with your Sway programs and the Fuel network. The @fuel-wallet packages include everything you need to interact with user wallets.

fuels requires Node version {props.nodeVersion}.

Install the following packages in your frontend folder:

<TestAction id="install-deps" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project/frontend' }} />

npm install fuels @fuels/react @fuels/connectors @tanstack/react-query

Generate contract types

The fuels init command generates a fuels.config.ts file that is used by the SDK to generate contract types. Use the contracts flag to define where your contract folder is located, and the output flag to define where you want the generated files to be created.

Run the command below in your frontend folder to generate the config file:

<TestAction id="fuels_config" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project/frontend' }} />

npx fuels init --contracts ../counter-contract/ --output ./src/sway-api

Now that you have a fuels.config.ts file, you can use the fuels build command to rebuild your contract and generate types. Running this command will interpret the output ABI JSON from your contract and generate the correct TypeScript definitions. If you see the folder fuel-project/counter-contract/out you will be able to see the ABI JSON there.

Inside the fuel-project/frontend directory run:

<TestAction id="typegen" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project/frontend' }} />

npx fuels build

A successful process should print and output like the following:

Building..
Building Sway programs using source 'forc' binary
Generating types..
🎉  Build completed successfully!

Now you should be able to find a new folder fuel-project/frontend/src/sway-api.

Modify the App

Inside the frontend/src folder let's add code that interacts with our contract.

Because we'll be using @fuels/react, first we need to wrap our app with the FuelProvider component.

Add the imports below to the top of your frontend/src/main.tsx file and setup a query client:

<TestAction id="provider-import" action={{ name: 'modifyFile', filepath: 'guides-testing/fuel-project/frontend/src/main.tsx', atLine: 5, }} />

Next, modify your frontend/src/main.tsx file to wrap the App component with the FuelProvider and QueryClientProvider components.

<TestAction id="fuel-wallet-provider" action={{ name: 'modifyFile', filepath: 'guides-testing/fuel-project/frontend/src/main.tsx', atLine: 11, removeLines: [11,12,13,14,15], }} />

Next, change the file fuel-project/frontend/src/App.tsx to:

<TestAction id="app-code" action={{ name: 'writeToFile', filepath: 'guides-testing/fuel-project/frontend/src/App.tsx' }} />

Finally, replace the value of the CONTRACT_ID variable at the top of your App.tsx file with the address of the contract you just deployed.

<TestAction id="app-contract-id" action={{ name: 'modifyFile', filepath: 'guides-testing/fuel-project/frontend/src/App.tsx', atLine: 13, removeLines: [13], useSetData: ' "0x92073699bd78dac70756a9e0e8bca1c7121c7adc4b90570800f0916fe4ac33dd";' }} />

Run your project

Inside the fuel-project/frontend directory run:

<TestAction id="start-app" action={{ name: 'runCommand', preCommand: "pnpm pm2 start 'BROWSER=none ' --name 'react-dapp' --cwd ./guides-testing/fuel-project/frontend" }} />

npm run dev
  VITE v5.3.5  ready in 108 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

Click the "Connect" button and select the wallet you have installed to connect your wallet.

Once connected, if there are no funds in your wallet, you will see a link to get testnet funds.

If you have testnet ETH on Fuel, you should see the counter value and increment button:

<Box.Centered> screenshot of the UI </Box.Centered>

You just built a fullstack dapp on Fuel! ⛽

Here is the repo for this project.

If you run into any problems, a good first step is to compare your code to this repo and resolve any differences.

Tweet us @fuel_network letting us know you just built a dapp on Fuel, you might get invited to a private group of builders, be invited to the next Fuel dinner, get alpha on the project, or something 👀.

Updating The Contract

To develop and test faster, we recommend using the fuels dev command to start a local node and automatically redeploy and generate types for your contract on each change.

Once you're ready to redeploy your contract to the testnet, here are the steps you should take to get your frontend and contract back in sync:

  • In your frontend directory, re-run this command: npx fuels build.
  • In your contract directory, redeploy the contract.
  • In your frontend directory, update the contract ID in your App.tsx file.

Need Help?

Get help from the team by posting your question in the Fuel Forum.

<TestAction id="wait-after-start-app" action={{ name: 'wait', timeout: 20000 }} />

<TestAction id="go-to-frontend" action={{ name: 'goToUrl', url: "http://localhost:5173" }} />

<TestAction id="click-connect-button" action={{ name: 'clickByRole', role: "button", elementName: "Connect" }} />

<TestAction id="click-fuel-wallet" action={{ name: 'clickByLabel', label: 'Connect to Fuel Wallet' }} />

<TestAction id="approve-connect" action={{ name: 'walletApproveConnect', }} />

<TestAction id="wait-after-connect" action={{ name: 'wait', timeout: 5000 }} />

<TestAction id="get-initial-count" action={{ name: 'getByLocator-save', locator: "h3 ~ div", }} />

{/* <TestAction id="click-increment-button" action={{ name: 'clickByRole', role: "button", elementName: "Increment" }} /> <TestAction id="approve-txn" action={{ name: 'walletApprove', }} />

<TestAction id="wait-after-approve" action={{ name: 'wait', timeout: 15000 }} />

<TestAction id="reload-after-approve" action={{ name: 'reload', }} />

<TestAction id="wait-after-reload" action={{ name: 'wait', timeout: 7000 }} />

<TestAction id="get-final-count" action={{ name: 'getByLocator-save', locator: "h3 ~ div", }} />

<TestAction id="check-count" action={{ name: 'checkIfIsIncremented', initialIndex: 0, finalIndex: 1 }} /> */}

Writing A Sway Smart Contract

Installation

{/install_help:example:start/}

Having problems? Visit the installation guide or post your question in our forum. {/install_help:example:end/}

Already have fuelup installed?

{/already_installed:example:start/} If you already have fuelup installed, run the commands below to make sure you are on the most up-to-date toolchain.

fuelup self update
fuelup update
fuelup default latest

{/already_installed:example:end/}

Your First Sway Project

We'll build a simple counter contract with two functions: one to increment the counter, and one to return the value of the counter.

Start by creating a new, empty folder. We'll call it fuel-project.

<TestAction id="create-project-folder" action={{ name: 'runCommand', commandFolder: 'guides-testing' }} />

mkdir fuel-project

Writing the Contract

Move inside of your fuel-project folder:

cd fuel-project

Then create a contract project using forc:

<TestAction id="create-contract" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project' }} />

{/ANCHOR: new_forc_contract/}

forc new counter-contract

{/ANCHOR_END: new_forc_contract/}

You will get this output:

To compile, use `forc build`, and to run tests use `forc test`
----
Read the Docs:
- Sway Book: https://docs.fuel.network/docs/sway
- Forc Book: https://docs.fuel.network/docs/forc
- Rust SDK Book: https://docs.fuel.network/docs/fuels-rs
- TypeScript SDK: https://docs.fuel.network/docs/fuels-ts

Join the Community:
- Follow us @SwayLang: https://twitter.com/SwayLang
- Ask questions on Discourse: https://forum.fuel.network/

Report Bugs:
- Sway Issues: https://github.com/FuelLabs/sway/issues/new

{/This example should include a tree for a new forc project and explain the boilerplate files/} {/forc_new:example:start/} Here is the project that forc has initialized:

<TestAction id="contract-tree" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project' }} />

tree counter-contract
counter-contract
├── Forc.toml
└── src
    └── main.sw

1 directory, 2 files

forc.toml is the manifest file (similar to Cargo.toml for Cargo or package.json for Node) and defines project metadata such as the project name and dependencies. {/forc_new:example:end/}

Open your project in a code editor and delete everything in src/main.sw apart from the first line.

Every Sway file must start with a declaration of what type of program the file contains; here, we've declared that this file is a contract. You can learn more about Sway program types in the Sway Book.

<TestAction id="program-type" action={{ name: 'writeToFile', filepath: 'guides-testing/fuel-project/counter-contract/src/main.sw' }} />

Next, we'll define a storage value. In our case, we have a single counter that we'll call counter of type u64 (a 64-bit unsigned integer) and initialize it to 0.

<TestAction id="storage" action={{ name: 'modifyFile', filepath: 'guides-testing/fuel-project/counter-contract/src/main.sw' }} />

ABI

ABI stands for Application Binary Interface. An ABI defines an interface for a contract. A contract must either define or import an ABI declaration.

It is considered best practice to define your ABI in a separate library and import it into your contract. This allows callers of the contract to import and use the ABI more easily.

For simplicity, we will define the ABI directly in the contract file itself.

<TestAction id="abi" action={{ name: 'modifyFile', filepath: 'guides-testing/fuel-project/counter-contract/src/main.sw' }} />

Implement ABI

Below your ABI definition, you will write the implementation of the functions defined in your ABI.

<TestAction id="impl" action={{ name: 'modifyFile', filepath: 'guides-testing/fuel-project/counter-contract/src/main.sw' }} />

storage.counter.read() is an implicit return and is equivalent to return storage.counter.read();.

Here's what your code should look like so far:

File: ./counter-contract/src/main.sw

<TestAction id="entire-contract" action={{ name: 'compareToFile', filepath: 'guides-testing/fuel-project/counter-contract/src/main.sw' }} />

Build the Contract

Navigate to your contract folder:

cd counter-contract

Then run the following command to build your contract:

<TestAction id="build-contract" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project/counter-contract' }} />

forc build
  Compiled library "core".
  Compiled library "std".
  Compiled contract "counter-contract".
  Bytecode size: 84 bytes.

Let's have a look at the content of the counter-contract folder after building:

<TestAction id="built-contract-tree" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project/counter-contract' }} />

tree .
.
├── Forc.lock
├── Forc.toml
├── out
│   └── debug
│       ├── counter-contract-abi.json
│       ├── counter-contract-storage_slots.json
│       └── counter-contract.bin
└── src
    └── main.sw

3 directories, 6 files

We now have an out directory that contains our build artifacts such as the JSON representation of our ABI and the contract bytecode.

Testing your Contract with Rust

Don't want to test with Rust? Skip this section and jump to Deploy the Contract.

We will start by adding a Rust integration test harness using a Cargo generate template. If you don't already have Rust installed, you can install it by running this command:

Next, if you don't already have it installed, let's install cargo generate:

cargo install cargo-generate --locked

Now, let's generate the default test harness with the following command:

<TestAction id="cargo-generate-test" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project/counter-contract' }} />

cargo generate --init fuellabs/sway templates/sway-test-rs --name counter-contract
⚠️   Favorite `fuellabs/sway` not found in config, using it as a git repository: https://github.com/fuellabs/sway.git
🔧   Destination: /home/user/path/to/counter-contract ...
🔧   project-name: counter-contract ...
🔧   Generating template ...
🔧   Moving generated files into: `/home/user/path/to/counter-contract`...
✨   Done! New project created /home/user/path/to/counter-contract

Let's have a look at the result:

<TestAction id="cargo-test-tree" action={{ name: 'runCommand', commandFolder: 'guides-testing/fuel-project/counter-contract' }} />

tree .
.
├── Cargo.toml
├── Forc.lock
├── Forc.toml
├── out
│   └── debug
│       ├── counter-contract-abi.json
│       ├── counter-contract-storage_slots.json
│       └── counter-contract.bin
├── src
│   └── main.sw
└── tests
    └── harness.rs

4 directories, 8 files

{/rust_harness:example:start/} We have two new files!

  • The Cargo.toml is the manifest for our new test harness and specifies the required dependencies including fuels (the Fuel Rust SDK).
  • The tests/harness.rs contains some boilerplate test code to get us started, though doesn't call any contract methods just yet.

Open your Cargo.toml file and check the version of fuels used under dev-dependencies. Change the version to 0.66.1 if it's not already:

[dev-dependencies]
fuels = "0.66.1"
tokio = { version = "1.12", features = ["rt", "macros"] }

{/rust_harness:example:end/}

Now that we have our default test harness, let's add a useful test to it.

At the bottom of test/harness.rs below the can_get_contract_id() test, add the test_increment test function below to verify that the value of the counter gets incremented:

<TestAction id="test-harness" action={{ name: 'modifyFile', filepath: 'guides-testing/fuel-project/counter-contract/tests/harness.rs', addSpacesBefore: 1, }} />

Here is what your file should look like:

File: ./counter-contract/tests/harness.rs

<TestAction id="final-test-harness" action={{ name: 'compareToFile', filepath: 'guides-testing/fuel-project/counter-contract/tests/harness.rs' }} />

Run cargo test in the terminal:

cargo test

If all goes well, the output should look as follows:

  ...
  running 2 tests
  test can_get_contract_id ... ok
  test test_increment ... ok
  test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.25s

Deploy the Contract

It's now time to deploy . We will show how to do this using forc from the command line, but you can also do it using the Rust SDK or the TypeScript SDK.

In order to deploy a contract, you need to have a wallet to sign the transaction and coins to pay for gas. Fuelup will guide you in this process.

Setting up a local wallet

You can get test funds using the faucet.

Deploy To Testnet

Now, you can deploy the contract to the latest testnet with the forc deploy command.

forc deploy --testnet

{/forc_wallet:example:start/} The terminal will ask for the password of the wallet:

Please provide the password of your encrypted wallet vault at "~/.fuel/wallets/.wallet":

Once you have unlocked the wallet, the terminal will show a list of the accounts:

Account 0 -- fuel18caanqmumttfnm8qp0eq7u9yluydxtqmzuaqtzdjlsww5t2jmg9skutn8n:
  Asset ID                                                           Amount
  0000000000000000000000000000000000000000000000000000000000000000 499999940

Just below the list, you'll see this prompt:

Please provide the index of account to use for signing:

Then you'll enter the number of the account of preference and press Y when prompted to accept the transaction.

Finally, you will get back the network endpoint where the contract was deployed, a Contract ID and the block where the transaction was signed. {/forc_wallet:example:end/}

Save the Contract ID, as you'll need this later to connect the frontend.

Contract counter-contract Deployed!

Network: https://testnet.fuel.network
Contract ID: 0x8342d413de2a678245d9ee39f020795800c7e6a4ac5ff7daae275f533dc05e08
Deployed in block 0x4ea52b6652836c499e44b7e42f7c22d1ed1f03cf90a1d94cd0113b9023dfa636

Congrats, you have completed your first smart contract on Fuel ⛽

Here is the repo for this project. If you run into any problems, a good first step is to compare your code to this repo and resolve any differences.

Tweet us @fuel_network letting us know you just built a dapp on Fuel, you might get invited to a private group of builders, be invited to the next Fuel dinner, get alpha on the project, or something 👀.

Need Help?

Get help from the team by posting your question in the Fuel Forum.

Checkpoint

If you have followed the steps properly, your predicate main.sw should look like the code below:

Building the predicate

To format your contract, execute the command:

<TestAction id="format-predicate" action={{ name: 'runCommand', commandFolder: 'guides-testing/multisig-predicate/predicate' }} />

forc fmt

To get the predicate root, go to the predicate folder and run:

<TestAction id="build-predicate" action={{ name: 'runCommand', commandFolder: 'guides-testing/multisig-predicate/predicate' }} />

forc build

Your predicate root should be exactly:

0x2d5e1058a695d6fd8bf30dfa1d8e987f99c9c99a6dd614103d2b4b0f11c1eb40

That's it! You've created your first stateless decentralized application, and we didn't even have to deploy it!

Checkpoint

Sway Contract Checkpoint

If you have followed the previous steps correctly your main.sw marketplace contract should look like this:

Building the contract

Here's a polished version of your instructions:

To format your contract, execute the command:

<TestAction id="format-contract" action={{ name: 'runCommand', commandFolder: 'guides-testing/sway-store/sway-programs/contract' }} />

forc fmt

To compile your contract, navigate to the contract folder and run:

<TestAction id="build-contract" action={{ name: 'runCommand', commandFolder: 'guides-testing/sway-store/sway-programs/contract' }} />

forc build

Congratulations! You've successfully written a full contract in Sway!

Post-compilation, the system will automatically generate abi.json, storage_slots.json, and contract.bin. You can locate these files in the following directory:

contract/out/debug/*

Deploying the contract

For detailed steps on deploying this contract, refer to the official Fuel developer counter dapp guide: Deploy the Contract

To deploy, use the following command if you've already set up the forc-wallet and have testnet funds in your account. If not, follow the instructions above.

forc deploy --testnet

After deploying, you'll be able to find your contract ID in the contract/out/deployments folder. You'll need this for frontend integration.

Configurables

Configurables are special constants that can be modified at compile time. This is where we can define the signers responsible for protecting the funds in the predicate as well as the number of signatures required.

This information can later be configured with SDKs before building a transaction.

<TestAction id="sway-configurable" action={{ name: 'modifyFile', filepath: 'guides-testing/multisig-predicate/predicate/src/main.sw' }} />

Imagine you are a multisig provider assisting businesses and users in setting up their own multisigs. You wouldn't want to hard-code these details every time but rather provide a few parameters that users can configure themselves.

Smart Contract Quickstart

Getting started with Fuel as a smart contract developer is as simple as:

  1. Installing fuelup
  2. Generating a counter contract
  3. Building the contract
  4. Setting up a local wallet
  5. Deploying the contract

Installation

Already have fuelup installed?

Generating a counter contract

Run the command below to generate a counter contract in Sway:

{/* TODO: Replace when forc template command is stable

forc template --template-name counter counter-contract

*/}

{/TODO: Remove when forc template command is stable/}

<TestAction id="create-contract-project" action={{ name: 'runCommand', commandFolder: 'guides-testing/' }} />

The contract will be in the src/main.sw file.

Building the contract

To build a contract, move inside the counter-contract folder:

cd counter-contract

{/TODO: Remove when forc template command is stable/}

Copy and paste the code below into your src/main.sw file

<TestAction id="copy-contract" action={{ name: 'writeToFile', filepath: 'guides-testing/counter-contract/src/main.sw' }} />

Next, run the forc build command:

<TestAction id="build-contract" action={{ name: 'runCommand', commandFolder: 'guides-testing/counter-contract' }} />

forc build

Setting up a local wallet

You can get test funds using the faucet.

Deploying the contract

To deploy the contract to the testnet, you can run:

forc deploy --testnet

Next Steps

Ready to learn more? Check out the following resources:

Counter React Dapp

This guide includes step-by-step instructions for how to

  • Write a counter smart contract in Sway
  • Write a test in Rust
  • Deploy to Fuel's testnet
  • Build a frontend
  • Integrate a wallet

Before we begin, it may be helpful to understand the terminology that will be used throughout the docs and how they relate to each other:

  • Fuel: the Fuel blockchain.
  • FuelVM: the virtual machine powering Fuel.
  • Sway: the domain-specific language crafted for the FuelVM; it is inspired by Rust.
  • Forc: the build system and package manager for Sway, similar to Cargo for Rust.

Debugging with Scripts

In every aspect of development, trade-offs are inevitable. As previously mentioned, logging is not feasible when dealing with predicates, since predicates are required to be pure. This raises an important question: how do we debug predicates?

Sway, a programming language, categorizes programs into four types, with scripts being one of them. Unlike predicates, scripts allow for shared logic.

Let's move outside our MultiSig project

cd ../..

and create a separate project called predicate-script-logging.

<TestAction id="create-predicate-script-logging" action={{ name: 'runCommand', commandFolder: 'guides-testing/' }} />

forc new --predicate predicate-script-logging

Copy and paste this new predicate in your src/main.sw. Attempting to build this predicate will result in an error, indicating that logging is an invalid operation.

However, let's try switching the program type from a predicate to a script.

Your code should now look like this:

<TestAction id="sway-program-type" action={{ name: 'writeToFile', filepath: 'guides-testing/predicate-script-logging/src/main.sw' }} />

Now, if we attempt to build our script, it should compile without any issues.

<TestAction id="build-predicate" action={{ name: 'runCommand', commandFolder: 'guides-testing/predicate-script-logging/' }} />

forc build

Next, we'll generate a Rust template to see it in action!

Defining Error Handling

Enumerations, commonly referred to as enums, are a type that can represent one of several possible variants. Within our contract, we can employ enums to craft custom error messages, facilitating more precise error handling within functions.

Copy the custom error block into your main.sw file:

<TestAction id="sway-errors" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/src/main.sw' }} />

Within our contract, we can anticipate various scenarios where we'd want to throw an error and halt the transaction:

  1. Someone might attempt to pay for an item using an incorrect currency.
  2. An individual could try to purchase an item without possessing sufficient coins.
  3. Someone other than the owner might attempt to withdraw funds from the contract.

For each situation, we can define specific return types for the errors:

  • For the IncorrectAssetId error, we can return the submitted asset id, which is of type AssetId.
  • In the case of the NotEnoughTokens error, we can define the return type as u64 to indicate the number of coins involved.
  • For the OnlyOwner error, we can utilize the Identity of the message sender as the return value.

Congrats on completing the intro to Sway guide! 🎉

Encountering issues? A useful initial step is to align your code with the repository's and address any discrepancies. Check out the project's repository here. 🔍

Excited about your achievement? Share it with us on Twitter @fuel_network. By doing so, you could gain access to an exclusive community of developers, receive an invitation to our upcoming Fuel dinner, or even get insider information about the project. Keep an eye out for surprises! 👀

Keep building on Fuel

Ready to keep building? You can dive deeper into Sway and Fuel in the resources below:

📘 Read the Sway Book

Build a frontend with the TypeScript SDK

🦀 Write tests with the Rust SDK

📖 See Example Sway Applications

🐦 Follow Sway Language on Twitter

👾 Join the Fuel Discord

Ask questions in the Fuel Forum

Next.js Fullstack Quickstart

A non-NextJs app will be available once this issue is resolved

Getting started with Fuel as a frontend or fullstack developer is as simple as:

  1. Installing fuelup
  2. Generating a counter dapp
  3. Running the project locally

Installation

Already have fuelup installed?

Generating a counter dapp

You can generate a full-stack counter dapp in seconds with the create fuels CLI:

pnpm create fuels
npm create fuels

Running the project locally

Move into the project directory. Assuming you named the project my-fuel-project, you can run:

cd my-fuel-project

Next, run the following command to start a local development node:

pnpm fuels:dev
npm run fuels:dev

The local endpoint for node will be http://localhost:4000/v1/graphql.

Next, open a new terminal in the project directory, and run the following command to start the frontend:

pnpm dev
npm run dev

The frontend will be running at http://localhost:3000.

While the local node is running, any changes you make to the Sway contract inside the sway-contract folder will automatically trigger several updates:

  1. The contract gets rebuilt using the forc build command.
  2. The contract will be redeployed to the local node using the forc deploy command .
  3. New TypeScript types for the contract and a file called contract-ids.json with the new contract ID will be generated in the src/sway-api folder.

This means you don't need to worry about updating the contract ID, ABI, or TypeScript types while you develop.

Next Steps

Ready to learn more? Check out the following resources:

Fuel Connectors

Fuel is built to enable connectivity with wallets from any chain. This is achieved through a tool called Fuel Connectors, which is used by developers to connect your wallet of choice with applications on the Fuel network.

Some behavior might surprise you if you've never worked with cross-chain wallets before, and this document walks through some of those surprises, and why they are completely normal.

Github Codespace

Introduction

The way to think about Github Codespaces is essentially VSCode in a browser. It’s a remote development environment that is extremely easy to spin up. While not all VS Code plugins are supported, the Sway LSP plugin is supported and works out of the box.

How to set up for a new repo

  1. Create a devcontainer.json file. The easiest way is by navigating to the repo and clicking Code → … → Configure dev container

    <Box.Centered> dev container walkthrough </Box.Centered>

  2. Edit the file to include the following features:

    "features": {
        "ghcr.io/devcontainers/features/common-utils:1": {},
        "ghcr.io/FuelLabs/devcontainer-features/fuelup:1.0.1": {},
    }
    
  3. Add any plugins that you want to be installed for this repo under “customizations”.

    "customizations": {
        "vscode": {
            "extensions": [
                "fuellabs.sway-vscode-plugin"
            ]
        }
    }
    

    Here are examples that include the Sway LSP plugin.

    3.1. https://github.com/FuelLabs/sway/blob/master/.devcontainer/devcontainer.json

    3.2. https://github.com/FuelLabs/quickstart/blob/master/.devcontainer/devcontainer.json

How to start a codespace

  1. Navigate to the repo that has Github Codespaces configured.

  2. Choose Code → Create codespace on master

    <Box.Centered> Create codespace walkthrough </Box.Centered>

  3. This will open a new tab with your codespace. It can take several minutes to start up.

    3.1. You now have a fully functional remote development environment with the Fuel toolchain installed! You can use forc to build and deploy Sway code, or fuelup to manage the toolchain version. You also have the Sway LSP plugin with full feature support for Sway, like syntax highlighting, hover docs, go-to definitions, etc.

    3.2. Note: if you are working on a large repository and find the codespace is running slow, you can configure it to use a larger instance by clicking Code → … → change machine type on a running instance, or starting a new instance with Code → … → New with options.

Pricing & billing

You will be required to enter billing information, however there is a substantial free tier.

What's next?

Now you are ready to start building with Fuel.

👉 Check out the counter dapp guide to deploy your first smart contract.

Defining the Contract Functions

Finally, it's time to compose our contract functions. Begin by copying and pasting the ABI we outlined earlier. It's crucial to ensure that the functions within the contract exactly align with the ABI; otherwise, the compiler will generate an error. Now, substitute the semicolons at the conclusion of each function with curly brackets. Also, modify abi SwayStore to impl SwayStore for Contract, as demonstrated below:

This guide will first show each of the completed functions above. Then, we'll break it down to explain each part, clarify specific syntax, and discuss fundamental concepts in Sway.

1. Listing an item

Our first function enables sellers to list an item for sale. They can specify the item's price and provide a string that references externally-stored data about the item.

Updating list storage

The initial step involves incrementing the item_counter in storage, which will serve as the item's ID. In Sway, all storage variables are contained within the storage keyword, ensuring clarity and preventing conflicts with other variable names. This also allows developers to easily track when and where storage is accessed or altered. The standard library in Sway provides read(), write(), and try_read() methods to access or manipulate contract storage. It's advisable to use try_read() when possible to prevent potential issues arising from accessing uninitialized storage. In this case, we read the current count of listed items, modify it, and then store the updated count back into storage, making use of the well-organized and conflict-free storage system.

When a function returns an Option or Result type, we can use unwrap() to access its inner value. For instance, try_read() returns an Option type. If it yields Some, we get the contained value; but if it returns None, the contract call is immediately halted.

Getting the message sender

Next, we'll retrieve the Identity of the account listing the item.

To obtain the Identity, utilize the msg_sender function from the standard library. The msg_sender represents the address of the entity (be it a user address or another contract address) initiating the current function call.

This function yields a Result, which is an enum type that can either be OK or an error. Use the Result type when anticipating a value that might result in an error. For example in the case of msg_sender when an external caller is involved and the coin input owners differ, identifying the caller becomes impossible. In such edge cases, an Err(AuthError) is returned.

enum Result<T, E> {
    Ok(T),
    Err(E),
}

In Sway, you can define a variable using either let or const.

To retrieve the inner value, you can use the unwrap method. It returns the contained value if the Result is OK and triggers a panic if the result indicates an error.

Creating a new item

You can instantiate a new item using the Item struct. Use the item_counter value from storage as the ID, set the price and metadata based on the input parameters, and initialize total_bought to 0.

Since the owner field requires an Identity type, you should utilize the sender value obtained from msg_sender().

Updating a StorageMap

Lastly, add the item to the item_map within storage using the insert method. Utilize the same ID for the key and designate the item as its corresponding value.

2. Buying an item

Next, we aim to allow buyers to purchase listed items. To achieve this, we'll need to:

  1. Accept the desired item ID from the buyer as a function parameter.
  2. Ensure the buyer is paying the correct price with valid coins.
  3. Increase the total_bought count for that item.
  4. Deduct a contract fee from the item's cost and transfer the remaining amount to the seller.

Verifying payment

We can use the msg_asset_id function from the standard library to obtain the asset ID of the coins being transferred in the transaction.

Next, we'll utilize the require statement to ensure the sent asset is the correct one.

The require statement accepts two arguments: a condition, and a value that's logged when the condition is false. Should the condition evaluate as false, the entire transaction is rolled back, leaving no changes.

In this case, the condition checks if the asset_id matches the base asset ID — the default asset associated with the base blockchain - using AssetId::base(). For example, if the base blockchain is Ethereum, the base asset would be ETH.

If there's a mismatch in the asset, for instance, if someone attempts to purchase an item using a different coin, we'll trigger the custom error previously defined, passing along the asset_id.

Next, we can use the msg_amount function from the standard library to retrieve the quantity of coins transmitted by the buyer within the transaction.

To ensure the sent amount is not less than the item's price, we should retrieve the item details using the item_id parameter.

To obtain a value for a specific key in a storage map, the get method is handy, wherein the key value is passed. For mapping storage access, the try_read() method is utilized. As this method produces a Result type, the unwrap method can be applied to extract the item value.

In Sway, all variables are immutable by default, whether declared with let or const. To modify the value of any variable, it must be declared mutable using the mut keyword. Since we plan to update the item's total_bought value, it should be defined as mutable.

Additionally, it's essential to ensure that the quantity of coins sent for the item isn't less than the item's price.

Updating buy storage

We can increase the item's total_bought field value and subsequently reinsert it into the item_map. This action will replace the earlier value with the revised item.

Transferring payment

Lastly, we can process the payment to the seller. It's recommended to transfer assets only after all storage modifications are completed to prevent reentrancy attacks.

For items reaching a specific price threshold, a fee can be deducted using a conditional if statement. The structure of if statements in Sway mirrors that in JavaScript except for the brackets ().

In the aforementioned if-condition, we assess if the transmitted amount surpasses 100,000,000. For clarity in large numbers like 100000000, we can represent it as 100_000_000. If the foundational asset for this contract is ETH, this equates to 0.1 ETH given that Fuel uses a 9 decimal system.

Should the amount exceed 0.1 ETH, a commission is determined and then deducted from the total.

To facilitate the payment to the item's owner, the transfer function is utilized. This function, sourced from the standard library, requires three parameters: the Identity to which the coins are sent, the coin's asset ID, and the coin quantity for transfer.

3. Get an item

To get the details for an item, we can create a read-only function that returns the Item struct for a given item ID.

To return a value in a function, you can use the return keyword, similar to JavaScript. Alternatively, you can omit the semicolon in the last line to return that value like in Rust.

fn my_function_1(num: u64) -> u64{
    // returns the num variable
    return num;
}

fn my_function_2(num: u64) -> u64{
    // returns the num variable
    num
}

4. Initialize the owner

This method sets the owner's Identity for the contract but only once.

To ensure that this function can only be called once, specifically right after the contract's deployment, it's imperative that the owner's value remains set to None. We can achieve this verification using the is_none method, which assesses if an Option type is None.

It's also important to note the potential risk of front running in this context this code has not been audited.

To assign the owner as the message sender, it's necessary to transform the Result type into an Option type.

Finally, we'll return the Identity of the message sender.

5. Withdraw funds

The withdraw_funds function permits the owner to withdraw any accumulated funds from the contract.

First, we'll ensure that the owner has been initialized to a specific address.

Next, we'll verify that the individual attempting to withdraw the funds is indeed the owner.

Additionally, we can confirm the availability of funds for withdrawal using the this_balance function from the standard library. This function returns the current balance of the contract.

Lastly, we'll transfer the entire balance of the contract to the owner.

6. Get the total items

The final function we'll introduce is get_count. This straightforward getter function returns the value of the item_counter variable stored in the contract's storage.

Review

The SwayStore contract implementation in your main.sw should now look like this, following everything else we have previously written:

<TestAction id="sway-functions" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/sway-programs/contract/src/main.sw' }} />

Imports

The predicate keyword is used to identify that the program is a predicate.

<TestAction id="sway-program-type" action={{ name: 'writeToFile', filepath: 'guides-testing/multisig-predicate/predicate/src/main.sw' }} />

We're going to utilize the Sway standard library in our predicate. Delete the template code except for the predicate keyword and copy in the imports below:

<TestAction id="sway-import" action={{ name: 'modifyFile', filepath: 'guides-testing/multisig-predicate/predicate/src/main.sw' }} />

Transactions

To construct the MultiSig, it's essential for us to obtain three specific components from the transaction through the standard library:

  1. Transaction Witness Data: We'll use this to attach signatures on the transaction.
  2. Transaction Witness Count: This will help us determine the number of signatures attached.
  3. Transaction ID: The hash of the transaction.

Constants

From the constants library, we'll be using ZERO_B256 as a placeholder.

Signatures

We'll need b512 because signatures are of type b512.

Elliptical Curve

Lastly, we will be using ec_recover_address, short for elliptical curve recovery address. It's a function that allows us to cryptographically recover the address that signed a piece of data:

#![allow(unused)]
fn main() {
signing_address = ec_recover_address(signed_data, original_data)
}

This step is crucial for safeguarding the funds and ensuring that only the correct wallets can provide the necessary signatures.

Imports

The Sway standard library provides several utility types and methods we can use in our contract. To import a library, you can use the use keyword and ::, also called a namespace qualifier, to chain library names like this:

You can also group together imports using curly brackets:

For this contract, here is what needs to be imported. Copy this to your main.sw file:

<TestAction id="sway-import" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/src/main.sw' }} />

We'll go through what each of these imports does as we use them in the next steps.

Toolchain installation

This guide will help you to install the Fuel toolchain binaries and prerequisites.

This guide covers the following topics:

  1. Installing the Fuel toolchain using fuelup
  2. Changing your default toolchain
  3. Setting up a local wallet
  4. Installing Rust

Installing the Fuel toolchain using fuelup

fuelup is the official package manager for Fuel that installs the Fuel toolchain from the official release channels, enabling you to easily switch between different toolchains and keep them updated. It makes building and maintaining Sway applications simpler with forc and fuel-core for common platforms.

💡 Check out the fuelup docs for more information.

Running fuelup-init

{/install_fuelup:example:start/} To install the Fuel toolchain, you can use the fuelup-init script. This will install forc, forc-client, forc-fmt, forc-lsp, forc-wallet as well as fuel-core in ~/.fuelup/bin. {/install_fuelup:example:end/}

👉 Just paste the following line in your terminal and press Enter.

{/ANCHOR: install_fuelup_command/}

curl https://install.fuel.network | sh

{/ANCHOR_END: install_fuelup_command/}

🚧 Be aware that currently we do not natively support Windows. If you wish to use fuelup on Windows, please use Windows Subsystem for Linux.

Setup PATH

Once the script is downloaded, it will be executed automatically. The fuelup-init script will prompt you with the question below:

fuelup uses "/home/username/.fuelup" as its home directory to manage the Fuel toolchain, and will install binaries there.

To use the toolchain, you will have to configure your PATH, which tells your machine where to locate `fuelup` and the Fuel toolchain.

If permitted, fuelup-init will configure your PATH for you by running the following:

    echo "export PATH="$HOME/.fuelup/bin:$PATH"" >> /home/username/.bashrc

Would you like fuelup-init to modify your PATH variable for you? (N/y)

👉 Press the Y key in your terminal and press Enter to modify your PATH.

Checking the installation

After allowing the fuelup-init script to modify your PATH variable, you will see a lot of information about packages being downloaded and installed. If everything goes as expected you will see the following message:

The Fuel toolchain is installed and up to date

fuelup 0.20.0 has been installed in /home/username/.fuelup/bin. 
To fetch the latest toolchain containing the forc and fuel-core binaries, run 'fuelup toolchain install latest'. 
To generate completions for your shell, run 'fuelup completions --shell=SHELL'.

👉 Use fuelup --version any time to check which version of the package you are using.

 fuelup --version

That will output your current fuelup version:

fuelup 0.21.0

VSCode extensions

{/install_VSCode_extensions:example:start/} If you're using VSCode, we recommend installing the Sway extension. {/install_VSCode_extensions:example:end/}

Changing your default toolchain

Just as in Rust, Fuel supports multiple toolchains. A toolchain is a collection of tools (such as the compiler, LSP, etc). By default, fuelup includes a series of packages tested to work with each other, providing a reliable set of tools.

The default toolchain configured when you install fuelup is the latest toolchain, which is the stable toolchain compatible with the current {props.fuelTestnetInlineCode} network.

Updating fuelup

Make sure you have the latest version of fuelup so you can access the latest features and have the best performance.

👉 Update fuelup by running the following command:

fuelup self update

Then you will get an output like this:

Fetching binary from https://github.com/FuelLabs/fuelup/releases/download/v0.19.5/fuelup-0.19.5-aarch64-apple-darwin.tar.gz
Downloading component fuelup without verifying checksum
Unpacking and moving fuelup to /var/folders/tp/0l8zdx9j4s9_n609ykwxl0qw0000gn/T/.tmpiNJQHt
Moving /var/folders/tp/0l8zdx9j4s9_n609ykwxl0qw0000gn/T/.tmpiNJQHt/fuelup to /Users/.fuelup/bin/fuelup

Using the latest toolchain

To properly interact with the testnet network it is necessary to use the latest toolchain, which is installed by default.

👉 Run the following command to verify the installation of latest toolchain:

fuelup show

If the toolchain was successfully installed, you will see this output:

installed toolchains
--------------------
latest-x86_64-unknown-linux-gnu (default)

active toolchain
-----------------
latest-x86_64-unknown-linux-gnu (default)
...

Installing nightly toolchain

In case you want to try out the unreleased features of the Fuel toolchain, you can install the nightly toolchain.

👉 Run the following command to install the nightly toolchain:

fuelup toolchain install nightly

If the toolchain was successfully installed, you will see this output:

The Fuel toolchain is installed and up to date

The toolchain was installed correctly, however is not in use yet. Next, you need to configure fuelup to use the nightly toolchain as the default.

{/set_default_testnet:example:start/} 👉 Set nightly as your default toolchain with the following command: {/set_default_testnet:example:end/}

{/ANCHOR: set_default_testnet_command/}

fuelup default nightly 

{/ANCHOR_END: set_default_testnet_command/}

You will get the following output indicating that you have successfully set nightly as your default toolchain.

default toolchain set to nightly

Checking your current toolchain

Sometimes you might end up using multiple toolchains at once. Don't worry if you get confused about which toolchain you are using, since you can check your current toolchain anytime.

👉 Run the fuelup show command to see the toolchain and the versions of each tool you are using.

fuelup show

This command will give you the following output

active toolchain
-----------------
beta-4-x86_64-unknown-linux-gnu (default)
  forc : 0.45.0
    - forc-client
      - forc-deploy : 0.45.0
      - forc-run : 0.45.0
    - forc-doc : 0.45.0
    - forc-explore : 0.28.1
    - forc-fmt : 0.45.0
    - forc-index : 0.20.7
    - forc-lsp : 0.45.0
    - forc-tx : 0.45.0
    - forc-wallet : 0.3.0
  fuel-core : 0.20.4
  fuel-core-keygen : Error getting version string

fuels versions
---------------
forc : 0.45
forc-wallet : 0.45

Setting up a local wallet

{/forc_wallet_setup:example:start/} The forc-wallet plugin is packaged alongside the default distributed toolchains when installed using fuelup, so you should already have this installed if you've followed the instructions above.

To initialize a new wallet with forc-wallet, you can run the command below:

forc wallet new

After typing in a password, be sure to save the mnemonic phrase that is output.

Next, create a new wallet account with:

forc wallet account new

With this, you'll get a fuel address that looks something like this: fuel1efz7lf36w9da9jekqzyuzqsfrqrlzwtt3j3clvemm6eru8fe9nvqj5kar8.

If you need to list your accounts, you can run the command below:

forc wallet accounts

{/forc_wallet_setup:example:end/}

Installing Rust

{/install_rust:example:start/} If you want to develop with the fuels Rust SDK, you will need to install Rust on your machine. To install Rust, you can use the rustup tool. {/install_rust:example:end/}

You don't need to install Rust if you don't plan on using the Rust SDK.

Run the following command in your shell; this downloads and runs rustup-init.sh, which in turn downloads and runs the correct version of the rustup-init executable for your platform.

{/ANCHOR: install_rust_command/}

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

{/ANCHOR: install_rust_command/}

Check the official Rust documentation to get more information on installing the Rust toolchain.

What's next?

Now you are ready to start building with Fuel.

👉 Check out the counter dapp guide to deploy your first smart contract.

Beyond the basics

Custom toolchains

You can create your own set of specific versions, this is known as 'custom toolchains'.

👉 Visit the Fuelup docs to learn how to set up your own custom toolchains.

Build from source

You can always build the Fuel packages from source.

👉 Visit the Fuelup docs to get more details about other types of installation.

Github Codespaces

It's always possible to run a development environment in the browser.

👉 Visit our guide on Github Codespaces to use the Fuel toolchain in the browser.

Predicates 101: Building Stateless DeFi Applications

Predicates are Fuel's approach to stateless account abstraction. In the blockchain space, we are constantly faced with the exponential growth of state bloat that just isn't sustainable in the long term. In the Ethereum ecosystem, every contract deployed requires state storage on the blockchain indefinitely. To help with blockchain scalability, we need to consider different approaches to redefine state-minimized applications that are fundamental to the world of decentralized finance.

This tutorial will specifically concentrate on the predicate program type, one of the four program types in the Sway language and how we can solve this ever growing problem.

What are Predicates?

To define Predicates into one sentence:

Predicates are stateless programs that return true or false.

A predicate is represented by an Address type, identical to any EOA (Externally Owned Account) created by a private key. The bytecode of the program can be deterministically hashed and represented as an ordinary address, all calculated off-chain. Therefore, when this address contains assets, ANYONE can spend the assets locked behind the predicate if they can evaluate the predicate to be true. It might be helpful to think of the code as the private key to the wallet.

If this concept is still a bit unclear don't worry!, let's explore a simple example in the next part of the project setup.

📚 Sway Standard Library: A native library equipped with useful types and methods.

🧑‍🔧 Fuelup: The official Fuel toolchain manager aids in installing and managing different versions.

🦀 Fuel's Rust SDK: Test and interact with your Sway contracts using Rust.

Fuel's TypeScript SDK: Test and interact with your Sway contracts using TypeScript.

Introduction to Sway Language for JavaScript Developers (React App)

If you're familiar with JavaScript and have a basic understanding of blockchain fundamentals, you can swiftly grasp how to build full-stack decentralized applications on Fuel using Sway. Once you get a handle on Sway's essentials, you'll be able to begin building your own dapp.

Within this tutorial, we will be crafting a Sway contract for an online marketplace similar to Amazon, where:

  1. Sellers can list products.
  2. Buyers can purchase those products.

<Box.Centered> intro to sway app </Box.Centered>

One of the compelling features of smart contracts is their immutability and permissionless nature. This ensures that no single entity can modify or adjust the rules of the marketplace after its deployment. For instance, once a product is listed in the contract, the deployer cannot suddenly alter its status. Similarly, if a commission amount is hardcoded into the contract, it remains fixed, preventing any changes to the commission charged for products.

Furthermore, the contract remains open for interaction by anyone. This universality allows any individual to engage with the marketplace using their custom frontend without requiring permission.

In this tutorial, our attention will be specifically directed towards the contract program type. This is just one of the four program types inherent to the Sway language.

What is Sway?

Sway is a strongly-typed programming language based on Rust, designed for authoring smart contracts on the Fuel blockchain. It leverages Rust's performance, control, and safety attributes, making it suitable for a blockchain virtual machine environment that's optimized for gas costs and contract security.

Sway is bolstered by a robust compiler and toolchain. These tools simplify the complexities and ensure that your code is efficient, secure, and performs optimally.

What truly distinguishes Sway is the exceptional suite of tools built around it. These tools are meticulously designed to convert contracts into full-stack dapps, ensuring a seamless and unparalleled developer experience.

📚 Sway Standard Library: A native library equipped with useful types and methods.

🧑‍🔧 Fuelup: The official Fuel toolchain manager aids in installing and managing different versions.

🦀 Fuel's Rust SDK: Test and interact with your Sway contracts using Rust.

Fuel's TypeScript SDK: Test and interact with your Sway contracts using TypeScript.

Logging in Rust tests

Generating a Test Template in Rust

To create your own test template using Rust, follow these steps with cargo-generate in the script project directory:

  1. Install cargo-generate:
cargo install cargo-generate --locked

{/markdownlint-disable/} 2. Generate the template: {/markdownlint-disable/}

<TestAction id="cargo-generate-test" action={{ name: 'runCommand', commandFolder: 'guides-testing/predicate-script-logging/' }} />

cargo generate --init fuellabs/sway templates/sway-test-rs --name sway-store

Logging

We previously covered imports and setting up the predicate in an earlier introduction to Sway tutorial, specifically in the Rust testing section. If you haven't checked that out yet, I highly recommend doing so.

Copy and paste the rust test below:

<TestAction id="sway-program-type" action={{ name: 'writeToFile', filepath: 'guides-testing/predicate-script-logging/tests/harness.rs' }} />

Now, I want to draw your attention to a specific portion of the code here:

We can now call decode_logs to extract our secret number, something we weren't able to do when testing with predicates.

To enable print outputs to appear in the console during tests, you can use the nocapture flag.

<TestAction id="cargo-test" action={{ name: 'runCommand', commandFolder: 'guides-testing/predicate-script-logging/' }} />

cargo test -- --nocapture

Remembering this method is essential when developing more complex predicates, especially as debugging becomes increasingly challenging.

Main

Now that we have all the components, let's put them together!

We simply call the function across all the multisigs, tallying the number of valid signatures to see if it meets the threshold set in the configuration. It must return true or false in order to determine if assets can be unlocked.

<TestAction id="sway-main" action={{ name: 'modifyFile', filepath: 'guides-testing/multisig-predicate/predicate/src/main.sw' }} />

Non-technical Guide

1. Your Wallet Address Will Look Different

This is expected, and it's because Fuel Connectors use a technical tool called a predicate to interact with your wallet. The address you see is the address of the predicate.

This predicate is kind of like a post office box that automatically forwards to your home address and visa versa. Anything you send from your wallet is relayed through the predicate, and anything you receive from elsewhere you receive through predicate.

The predicate is audited for secure. And your wallet address is still your wallet address. You will just see the predicate address on Fuel.

2. Funds Display in Wallets

For some Ethereum VM (EVM) or Solana VM (SVM) wallets, funds will not display in your wallet directly, unless you are interacting with Fuel native applications.

If you see this, don’t panic! Your funds are there and you can move them by following the following steps.

Step 1: Go to Fuel Explorer

step one

Step 2: Connect your wallet

step two

Step 3: Connect a non-native wallet

step three

Step 4: Open drop-down and select “My Account”

step four

Step 5: View your account and assets

step five

Note: This will improve over time as connections between the Fuel Network and wallets are built into the wallets themselves.

3. Blind Signing

Wallets that are built for other chains (EVM or SVM, for example) will use a technique called "Blind signing" to sign transactions. There is some risk to this, and you should only interact with trusted dApps using your non-native wallet.

Conclusion

You can connect essentially any EVM or SVM wallet to Fuel via Fuel Connectors, and you can use them on the network securely. Just be aware of the above experiences as you do so.

Other Options

If you are uncomfortable with any of these experiences or risks, you can use a Fuel Native wallet. A list of trusted Fuel Native wallets is available here.

Predicate Limitations

Given that all operations are done off-chain, it's clear there are inherent limitations to the capabilities of a predicate.

PredicatesContracts
Access data on chain
Read data from smart contracts
Check date or time
Read block hash or number
Read input coins
Read output coins
Read transaction scripts
Read transaction bytecode

These constraints mean that our MultiSig cannot "approve" interactions with contracts. However, it retains the ability to oversee the flow of funds when the correct wallets are involved.

Now that we have a basic understanding of what a predicate is, we can start writing our MultiSig.

Predicate Root

Let's pause here for a moment and build the predicate at the root of the predicate folder.

<TestAction id="build-predicate" action={{ name: 'runCommand', commandFolder: 'guides-testing/multisig-predicate/predicate' }} />

forc build

Unlike building a contract, constructing the predicate generates an additional piece of information: an address that is hashed from the predicate code of your templated project, known as the predicate root. Since this process is cryptographic, any changes to the code will result in a change in the predicate root.

Since everyone is starting with the exact same templated code, the predicate root should be exactly this:

0x68fec7a57e48f4ec6467d7e09c27272bd8ca72b312ea553a470b98731475ccf3

Looking at the predicate, you can immediately notice several differences. There is no ABI or implementation, but simply a main function that returns true or false.

Notice that we have not "deployed" anything on the Fuel blockchain, yet we already have an address that we can interact with. It is important to remember this:

Predicates are created, not deployed.

Prerequisites

Installation

Already have fuelup installed?

Project Setup

Start with a new empty folder and name it multisig-predicate.

<TestAction id="create-project-folder" action={{ name: 'runCommand', commandFolder: 'guides-testing' }} />

mkdir multisig-predicate

Go into the multisig-predicate folder:

cd multisig-predicate

Within your terminal start by creating a new sway project called predicate:

<TestAction id="create-predicate" action={{ name: 'runCommand', commandFolder: 'guides-testing/multisig-predicate' }} />

forc new --predicate predicate

Tip: Notice the --predicate flag, which tells forc that you want to create a project based on a predicate, rather than the default contract program type.

Your project structure generated from the forc command should like this:

<TestAction id="predicate-tree" action={{ name: 'runCommand', commandFolder: 'guides-testing/multisig-predicate' }} />

tree predicate
predicate
├── Forc.toml
└── src
    └── main.sw

1 directory, 2 files

Move into your predicate folder:

cd predicate

In VSCode, navigate to the src folder within the predicate folder, where you will find a file named main.sw. This is the file where your Sway predicate will be written.

Prerequisites

Installation

Already have fuelup installed?

Fuel Wallet

Additionally for this guide, ensure you're using Node.js/npm version {props.nodeVersion}. You can check your Node.js version with:

node -v

Project Setup

Start with a Fuel template and name it sway-store.

<TestAction id="create-project-folder" action={{ name: 'runCommand', commandFolder: 'guides-testing' }} />

pnpm create fuels --pnpm sway-store

Go into the sway-store folder:

cd sway-store

There should already be a folder called sway-programs inside, where your Sway programs will live. Ignore the other programs as we will only focus on the contract program type in this tutorial. Move into your contract folder:

cd sway-programs/contract

Open up the contract folder in VSCode, and inside the src folder you should see a file called main.sw. This is where you will write your Sway contract.

Since we're creating a brand new contract you can delete everything in this file except for the contract keyword.

<TestAction id="sway-program-type" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/sway-programs/contract/src/main.sw' }} />

The first line of the file is specifically reserved to inform the compiler whether we are writing a contract, script, predicate, or library. To designate the file as a contract, use the contract keyword.

Testing the predicate

Let's jump back into our MultiSig project again!

cd ../../multisig-predicate/predicate

Generating a Test Template in Rust

Again follow these steps with cargo-generate in the predicate project directory like we did previously:

  1. Install cargo-generate:
cargo install cargo-generate --locked

{/markdownlint-disable/} 2. Generate the template: {/markdownlint-disable/}

<TestAction id="cargo-generate-test" action={{ name: 'runCommand', commandFolder: 'guides-testing/multisig-predicate/predicate/' }} />

cargo generate --init fuellabs/sway templates/sway-test-rs --name sway-store

{/markdownlint-disable/} 3. Update the Cargo.toml file {/markdownlint-disable/}

<TestAction id="temp-update-cargo-toml-file" action={{ name: 'writeToFile', filepath: 'guides-testing/multisig-predicate/predicate/Cargo.toml' }} />

Imports

Delete the templated code and copy the following imports into your harness file. It's important to pay attention to two main imports: predicates, for obvious reasons, and the ScriptTransactionBuilder, which we'll use to create transactions. These transactions must be signed before being broadcasted to our local network.

<TestAction id="multisig-predicate-test-imports" action={{ name: 'writeToFile', filepath: 'guides-testing/multisig-predicate/predicate/tests/harness.rs' }} />

Similar to Rust testing for contracts, we'll import the predicate ABI (Application Binary Interface) to interact with it. Ensure the name of your predicate matches the one you're working with.

<TestAction id="multisig-predicate-test-abi" action={{ name: 'modifyFile', filepath: 'guides-testing/multisig-predicate/predicate/tests/harness.rs' }} />

Setup

If you're familiar with Rust testing for Sway projects, much of the setup will be similar. Copy and paste the setup_wallets_and_network function into your harness file.

<TestAction id="multisig-predicate-test-setup" action={{ name: 'modifyFile', filepath: 'guides-testing/multisig-predicate/predicate/tests/harness.rs' }} />

The three key setup steps include:

  1. Configuring the wallets that will act as owners of our multisig, through the configurables you'll see later in the tests.

{/markdownlint-disable/} 2. Setting up the default token (zeroth address) and loading some tokens into each wallet. {/markdownlint-disable/}

{/markdownlint-disable/} 3. Preparing the network to broadcast our transaction, enabling us to successfully unlock the tokens from the predicate later. {/markdownlint-disable/}

Since the predicate address is deterministic, we don't need to copy it as we do with smart contracts, which are deployed with a different address each time. We can leverage SDKs to build the predicate, ensuring we're working with the correct address without error!

{/markdownlint-disable/} 4. Gas isn't just used by the script itself; you also pay for the size of the transaction, signature checks, VM initialization, etc. These costs do not count towards the script gas so it might be hidden. {/markdownlint-disable/}

Test Cases

Valid 2 of 3 signatures

Now, let's review the sequence of actions we'll take to simulate a real-world scenario, copy and paste the first test below and let's break it down step by step:

<TestAction id="multisig-predicate-test-valid-two-of-three" action={{ name: 'modifyFile', filepath: 'guides-testing/multisig-predicate/predicate/tests/harness.rs' }} />

  1. A group or individuals create their multisig by specifying the wallets that will safeguard the funds.
  2. Funding the predicate.
  3. Extracting the tokens when needed by building a transaction and getting the original wallets to sign it.
  4. Broadcasting the transaction to unlock the funds from the predicate.

For step 1, as mentioned earlier, when we configure the number of required signatures (up to 3) and the 3 addresses that will safeguard our funds. Importing the ABI will automatically load a PredicateNameConfigurable type. In our case, that will be MultiSigConfigurables. There will be a corresponding with_configurable function to help you load each configurable. In our case, with_REQUIRED_SIGNATURES and with_SIGNERS are both loaded in!

How convenient!

Next, we'll load our original predicate binary with our new configurables to generate our personalized predicate instance. Simply input your configurables using the with_configurables function, and this will give us a unique predicate root based on our inputs.

For step 2, transferring funds to our newly generated predicate root is as straightforward as any other blockchain transfer.

In step 3, when the multisig holders decide to use the locked funds, we build a transaction specifying the inputs and outputs. Pay close attention to the outputs; we need to specify where the tokens from the predicate are going, which native asset they involve, and the amount. We're essentially extracting a portion of the original base asset sent into the predicate.

The correct wallet addresses configured in the configurables must sign the transactions. This information, loaded as witness data, will evaluate our predicate to true. It's crucial to provide enough correct, unique signatures; otherwise, the transaction will fail, as demonstrated in later tests. Since our test only requires 2 signatures, we need to provide just those.

After the evaluation is correctly done, all we need to do is broadcast the transaction, and the requested funds should return to wallet 1.

Valid unordered 3 of 3 signatures

The setup for the second test, multisig_mixed_three_of_three, follows the same scheme, showcasing that the transaction signing can be done in any order by valid wallets.

<TestAction id="multisig-predicate-test-valid-3-of-3" action={{ name: 'modifyFile', filepath: 'guides-testing/multisig-predicate/predicate/tests/harness.rs' }} />

Insufficient valid Signatures

The same principle applies to the third test, multisig_not_enough_signatures_fails, where the transaction will fail if there aren't enough signatures.

<TestAction id="multisig-predicate-test-insufficient-1-of-3" action={{ name: 'modifyFile', filepath: 'guides-testing/multisig-predicate/predicate/tests/harness.rs' }} />

Checkpoint

If you have followed the previous steps correctly, your harness.rs test file should look like this:

Running the Tests

To run the test located in tests/harness.rs, use:

<TestAction id="cargo-test" action={{ name: 'runCommand', commandFolder: 'guides-testing/multisig-predicate/predicate/' }} />

cargo test

If you want to print outputs to the console during tests, use the nocapture flag:

cargo test -- --nocapture

Congratulations on making it this far! We've confirmed that our Multisig works.

Predicates aren't meant to be intimidating. State-minimized DeFi applications should be the standard, rather than resorting to gas golfing or writing assembly code for these optimizations. Now that you have predicates in your toolbox, go out and explore what other state-minimized DeFi applications you can build!

Testing the contract

Generating a Test Template in Rust

To create your own test template using Rust, follow these steps with cargo-generate in the contract project directory:

  1. Install cargo-generate:
cargo install cargo-generate --locked

{/markdownlint-disable/} 2. Generate the template: {/markdownlint-disable/}

<TestAction id="cargo-generate-test" action={{ name: 'runCommand', commandFolder: 'guides-testing/sway-store/sway-programs/contract' }} />

cargo generate --init fuellabs/sway templates/sway-test-rs --name sway-store

{/markdownlint-disable/} 3. Update the Cargo.toml file {/markdownlint-disable/}

<TestAction id="temp-update-cargo-toml-file" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/sway-programs/contract/Cargo.toml' }} />

Imports

We will be changing the existing harness.rs test file that has been generated. Firstly we need to change the imports. By importing the Fuel Rust SDK you will get majority of the functionalities housed within the prelude.

<TestAction id="harness-import" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/sway-programs/contract/tests/harness.rs' }} />

Always compile your contracts after making any changes. This ensures you're working with the most recent contract-abi that gets generated.

Update your contract name and ABI path in the abigen macro to match the name of your contract:

<TestAction id="harness-abi" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/tests/harness.rs' }} />

Initializing Functions

When writing tests for Sway, two crucial objects are required: the contract instance and the wallets that interact with it. This helper function ensures a fresh start for every new test case so copy this into your test file. It will export the deployed contracts, the contract ID, and all the generated wallets for this purpose.

Replace the get_contract_instance function in your test harness with the function below:

<TestAction id="harness-instance" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/tests/harness.rs' }} />

Test Cases

Given the immutable nature of smart contracts, it's important to cover all potential edge cases in your tests. Let's delete the example can_get_contract_id test case and start writing some test cases at the bottom of our harness.rs file.

Setting Owner

For this test case, we use the contract instance and use the SDK's .with_account() method. This lets us impersonate the first wallet. To check if the owner has been set correctly, we can see if the address given by the contract matches wallet 1's address. If you want to dig deeper, looking into the contract storage will show if wallet 1's address is stored properly.

<TestAction id="harness-test-set-owner" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/tests/harness.rs' }} />

Setting Owner Once

An edge case we need to be vigilant about is an attempt to set the owner twice. We certainly don't want unauthorized ownership transfer of our contract! To address this, we've included the following line in our Sway contract: require(owner.is_none(), "owner already initialized"); This ensures the owner can only be set when it hasn't been previously established. To test this, we create a new contract instance: initially, we set the owner using wallet 1. Any subsequent attempt to set the owner with wallet 2 should be unsuccessful.

<TestAction id="harness-test-set-owner-once" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/tests/harness.rs' }} />

Buying and Selling in the Marketplace

It's essential to test the basic functionalities of a smart contract to ensure its proper operation. For this test, we have two wallets set up:

  1. The first wallet initiates a transaction to list an item for sale. This is done by calling the .list_item() method, specifying both the price and details of the item they're selling.
  2. The second wallet proceeds to purchase the listed item using the .buy_item() method, providing the index of the item they intend to buy.

Following these transactions, we'll assess the balances of both wallets to confirm the successful execution of the transactions.

<TestAction id="harness-test-buy-sell" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/tests/harness.rs' }} />

Withdraw Owner Fees

Most importantly, as the creator of the marketplace, you need to ensure you're compensated. Similar to the previous tests, we'll invoke the relevant functions to make an exchange. This time, we'll verify if you can extract the difference in funds.

<TestAction id="harness-test-owner-withdraw" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/tests/harness.rs' }} />

Checkpoint

If you have followed the previous steps correctly your harness.rs test file should look like this:

Running the Tests

{/markdownlint-disable/} Update the shared fuel-toolchain.toml file {/markdownlint-disable/}

<TestAction id="temp-update-fuel-toolchain-toml-file" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/fuel-toolchain.toml' }} />

To run the test located in tests/harness.rs, run the command below inside your contract folder:

<TestAction id="cargo-test" action={{ name: 'runCommand', commandFolder: 'guides-testing/sway-store/sway-programs/contract' }} />

cargo test

If you want to print outputs to the console during tests, use the nocapture flag:

cargo test -- --nocapture

Now that we're confident in the functionality of our smart contract, it's time to build a frontend. This will allow users to seamlessly interact with our new marketplace!

Signature Verification

Let's define a helper function called verify_signatures() that checks the validity of each signature provided and rejects any that are invalid, ensuring all signatures are unique.

Copy the signature verification helper function in your main.sw below:

<TestAction id="sway-function" action={{ name: 'modifyFile', filepath: 'guides-testing/multisig-predicate/predicate/src/main.sw' }} />

As mentioned earlier, we will utilize the transaction witnesses and the transaction hash to verify each signature, matching them to the wallets that were previously configured.

  1. Parameter i: This parameter represents the index of a signer in a predefined list of signers defined in the configurable. It's used to identify which signer's signature the function is currently attempting to verify.

  2. Signature Verification Loop: The function then enters a loop, iterating up to three times. This loop represents an attempt to verify the signature against up to three pieces of witness data (signatures) attached to the transaction.

    • Signature Recovery: Inside the loop, for each iteration defined by j, it retrieves the current signature (current_signature) from the transaction's witness data using tx_witness_data::<B512>(j). It then attempts to recover the address that generated this signature (current_address) by using the ec_recover_address function, which takes the current signature and the transaction hash as inputs.

    • Address Matching: After recovering the address, the function checks if this address matches the address of the ith signer in the SIGNERS list. If a match is found, it means the signature from one of the signers has been successfully verified, and the function returns 1.

{/markdownlint-disable/} 3. Return Value: The function returns 1 if a matching signature is found for the ith signer, indicating successful verification. If no matching signature is found after checking up to three signatures, the function returns 0, indicating that the signature for the ith signer could not be verified. {/markdownlint-disable/}

This allows for flexible signature verification, accommodating scenarios where signatures from the required signers can be presented in any order and ensuring that each signature is uniquely accounted for without allowing duplicates from the same wallet.

Defining The Storage Block

Next, we'll introduce the storage block. This is where you store all persistent state variables in your contract.

Variables declared within a function and not saved in the storage block will be discarded once the function completes its execution. Add the storage block below to your main.sw file:

<TestAction id="sway-storage" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/src/main.sw' }} />

The first variable we've stored is item_counter, a number initialized to 0. This counter can be used to track the total number of items listed.

StorageMap

A StorageMap is a unique type that permits the saving of key-value pairs within a storage block.

To define a storage map, you need to specify the types for both the key and the value. For instance, in the example below, the key type is u64, and the value type is an Item struct.

Here, we are creating a mapping from the item's ID to the Item struct. Using this, we can retrieve information about an item using its ID.

Options

Here, we are defining the owner variable as one that can either be None or hold an Identity.

If you want a value to be potentially null or undefined under specific conditions, you can employ the Option type. It's an enum that can take on either Some(value) or None. The keyword None indicates the absence of a value, while Some signifies the presence of a stored value.

Defining an Item Struct

Struct is short for structure, which is a data structure similar to an object in JavaScript. You define a struct with the struct keyword in Sway and define the fields of a struct inside curly brackets.

The core of our program is the ability to list, sell, and get items.

Let's define the Item type as shown below to write into your main.sw file:

<TestAction id="sway-struct" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/sway-programs/contract/src/main.sw' }} />

The item struct will contain an ID, price, the owner's identity, a string representing a URL or identifier for off-chain data about the item (such as its description and photos), and a "total bought" counter to track the overall number of purchases.

Types

The Item struct uses three types: u64, str, and Identity.

u64: a 64-bit unsigned integer.

In Sway, there are four native types of numbers:

  • u8: an 8-bit unsigned integer.
  • u16: a 16-bit unsigned integer.
  • u32: a 32-bit unsigned integer.
  • u64: a 64-bit unsigned integer.
  • u256: a 256-bit unsigned integer.

An unsigned integer means there is no + or - sign, making the value always positive. u64 is the default type used for numbers in Sway.

In JavaScript, there are two types of integers: number and BigInt. The primary difference between these types is that BigInt can store much larger values. Similarly, each numeric type in Sway has its maximum value that can be stored.

String Array: a string is a built-in primitive type in Sway. The number inside the square brackets indicates the size of the string.

Identity: an enum type that represents either a user's Address or a ContractId. In Sway, a contract and an EOA (Externally Owned Account) are distinctly differentiated. Both are type-safe wrappers for b256.

Building the Frontend

Generate contract types

In your folder you have a fuels.config.ts file, you can use the fuels build command to rebuild your contract and generate types. Running this command will interpret the output ABI JSON from your contract and generate the correct TypeScript definitions. If you see the folder sway-store/counter-contract/out you will be able to see the ABI JSON there.

Inside the sway-store/src directory run:

<TestAction id="typegen" action={{ name: 'runCommand', commandFolder: 'guides-testing/sway-store/' }} />

npx fuels build

A successful process should print and output like the following:

Building..
Building Sway programs using built-in 'forc' binary
Generating types..
🎉  Build completed successfully!

Now you should be able to find a new folder sway-store/src/sway-api.

Wallet Providers

In your main.tsx file, wrap your App component with the FuelProvider and QueryClientProvider components to enable Fuel's custom React hooks for wallet functionalities.

This is where you can pass in custom wallet connectors to customize which wallets your users can use to connect to your app.

<TestAction id="fe-index-all" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/src/main.tsx' }} />

Connecting to the contract

Next, open the src/App.tsx file, and replace the boilerplate code with the template below:

<TestAction id="fe-app-template" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/src/App.tsx' }} />

At the top of the file, change the CONTRACT_ID to the contract ID that you deployed earlier and set as a constant.

React hooks from the @fuels/react package are used in order to connect our wallet to the dapp. In the App function, we can call these hooks like this:

The wallet variable from the useWallet hook will have the type FuelWalletLocked.

You can think of a locked wallet as a user wallet you can't sign transactions for, and an unlocked wallet as a wallet where you have the private key and are able to sign transactions.

The useMemo hook is used to connect to our contract with the connected wallet.

Styling

Copy and paste the CSS code below in your App.css file to add some simple styling.

<TestAction id="fe-css-template" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/src/App.css' }} />

UI

In our app we're going to have two tabs: one to see all of the items listed for sale, and one to list a new item for sale.

We use another state variable called active that we can use to toggle between our tabs. We can set the default tab to show all listed items.

Next we can create our components to show and list items.

Listing an Item

Inside components, create a file inside called ListItem.tsx.

<TestAction id="create-list-item-file" action={{ name: 'runCommand', commandFolder: 'guides-testing/sway-store/src/components' }} />

touch ListItem.tsx

At the top of the file, import the useState hook from react, the generated contract ABI from the contracts folder, and bn (big number) type from fuels.

<TestAction id="fe-list-item-import" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/src/components/ListItem.tsx' }} />

This component will take the contract we made in App.tsx as a prop, so let's create an interface for the component.

<TestAction id="fe-list-item-interface" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/ListItem.tsx' }} />

We can set up the template for the function like this.

<TestAction id="fe-list-item-list-item" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/ListItem.tsx' }} />

To list an item, we'll create a form where the user can input the metadata string and price for the item they want to list. Let's start by adding some state variables for the metadata and price. We can also add a status variable to track the submit status.

<TestAction id="fe-list-item-state-variables" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/ListItem.tsx' }} />

We need to add the handleSubmit function. We can use the contract prop to call the list_item function and pass in the price and metadata from the form.

<TestAction id="fe-list-item-handle-submit" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/ListItem.tsx' }} />

Under the heading, add the code below for the form:

<TestAction id="fe-list-item-return-form" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/ListItem.tsx' }} />

Now, try listing an item to make sure this works. You should see the message Item successfully listed!.

Show All Items

Next, let's create a new file called AllItems.tsx in the components folder.

<TestAction id="create-all-item-file" action={{ name: 'runCommand', commandFolder: 'guides-testing/sway-store/src/components' }} />

touch AllItems.tsx

Copy and paste the template code below for this component:

<TestAction id="fe-all-item-template" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/src/components/AllItems.tsx' }} />

Here we can get the item count to see how many items are listed, and then loop through each of them to get the item details.

First, let's create some state variables to store the number of items listed, an array of the item details, and the loading status.

<TestAction id="fe-all-item-state-variables" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/AllItems.tsx' }} />

Next, let's fetch the items in a useEffect hook. Because these are read-only functions, we can simulate a dry-run of the transaction by using the get method instead of call so the user doesn't have to sign anything.

<TestAction id="fe-all-item-use-effect" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/AllItems.tsx' }} />

If the item count is greater than 0 and we are able to successfully load the items, we can map through them and display an item card.

The item card will show the item details and a buy button to buy that item, so we'll need to pass the contract and the item as props.

<TestAction id="fe-all-item-cards" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/AllItems.tsx' }} />

Item Card

Now let's create the item card component. Create a new file called ItemCard.tsx in the components folder.

<TestAction id="create-item-card-file" action={{ name: 'runCommand', commandFolder: 'guides-testing/sway-store/src/components/' }} />

touch ItemCard.tsx

After, copy and paste the template code below.

<TestAction id="fe-item-card-template" action={{ name: 'writeToFile', filepath: 'guides-testing/sway-store/src/components/ItemCard.tsx' }} />

Add a status variable to track the status of the buy button.

<TestAction id="fe-item-card-status" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/ItemCard.tsx' }} />

Create a new async function called handleBuyItem. Because this function is payable and transfers coins to the item owner, we'll need to do a couple special things here.

Whenever we call any function that uses the transfer or mint functions in Sway, we have to append the matching number of variable outputs to the call with the txParams method. Because the buy_item function just transfers assets to the item owner, the number of variable outputs is 1.

Next, because this function is payable and the user needs to transfer the price of the item, we'll use the callParams method to forward the amount. With Fuel you can transfer any type of asset, so we need to specify both the amount and the asset ID.

<TestAction id="fe-item-card-buy-item" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/ItemCard.tsx' }} />

Then add the item details and status messages to the card.

<TestAction id="fe-item-cards" action={{ name: 'modifyFile', filepath: 'guides-testing/sway-store/src/components/ItemCard.tsx' }} />

Now you should be able to see and buy all of the items listed in your contract.

Checkpoint

Ensure that all your files are correctly configured by examining the code below. If you require additional assistance, refer to the repository here

App.tsx

AllItems.tsx

ItemCard.tsx

ListItem.tsx

Run your project

Inside the fuel-project/frontend directory run:

<TestAction id="start-app" action={{ name: 'runCommand', preCommand: "pnpm pm2 start 'PORT=4000 BROWSER=none ' --name 'react-dapp' --cwd ./guides-testing/fuel-project/frontend" }} />

npm start
Compiled successfully!

You can now view frontend in the browser.

  Local:            http://localhost:3000
  On Your Network:  http://192.168.4.48:3000

Note that the development build is not optimized.
To create a production build, use npm run build.

And that's it for the frontend! You just created a whole dapp on Fuel!

{/TODO: MAKE THIS MORE RELIABLE/} {/* <TestAction id="wait-after-start-app" action={{ name: 'wait', timeout: 5000 }} />

<TestAction id="go-to-frontend" action={{ name: 'goToUrl', url: "http://localhost:4000" }} />

<TestAction id="click-connect-button" action={{ name: 'clickByRole', role: "button", elementName: "Connect" }} />

<TestAction id="click-fuel-wallet" action={{ name: 'clickByLabel', label: 'Connect to Fuel Wallet' }} />

<TestAction id="approve-connect" action={{ name: 'walletApproveConnect', }} />

<TestAction id="wait-after-connect" action={{ name: 'wait', timeout: 5000 }} />

<TestAction id="click-buy-item-button" action={{ name: 'clickByTestId', testId: "buy-button-1" }} />

<TestAction id="approve-txn" action={{ name: 'walletApprove', }} />

<TestAction id="wait-after-approve" action={{ name: 'wait', timeout: 5000 }} />

<TestAction id="check-text" action={{ name: 'getByLocator-save', locator: "h3 ~ div", }} />

<TestAction id="wait-after-buy" action={{ name: 'wait', timeout: 18000 }} />

<TestAction id="check-if-purchased" action={{ name: 'checkValue', index: 0, value: 'Purchased ✅' }} />*/}

Fuel Glossary

Address

An address is a cryptographic hash representing an identity of a wallet or a predicate root.

AssetId

An asset ID is a unique identifier for an on-chain asset. It is derived from the root of the bytecode of the contract minting the asset.

Base Asset

The base asset is the underlying asset needed to perform any transactions on a blockchain. It is used to pay gas for transactions. On Ethereum, the base asset is ETH.

Block

A block is a record of many transactions, that are grouped together and cryptographically hashed. The linked blocks form a chain, also called a blockchain.

Block Explorer

A block explorer is an interface for block and transaction data produced by a blockchain. You can use a block explorer to explore and verify addresses, contracts, and transaction histories.

Block Height

The block height refers to the total number of valid blocks produced in the history of a blockchain, starting with the genesis block.

Block ID

A block ID is a unique identifier for a particular block.

Bridge

A bridge is a mechanism that allows the transfer of data or assets from one blockchain to another.

Bytecode

Bytecode is machine-readable code, usually generated from a compiler.

Chain

Another name for a blockchain.

ChainId

A unique ID for a blockchain.

Client

The Fuel client refers to the software that runs the Fuel Virtual Machine. It can be used to validate and produce blocks, and send transactions.

Coinbase

Coinbase refers to the validators paying themselves for processing a block from the transaction fees. Having a coinbase transaction on each block makes this process transparent to all users.

Consensus

The consensus layer defines the state and validates that all nodes on the blockchain have the same state.

Consensus Parameters

Consensus parameters are the rules used by clients to determine the validity of and finalize a block.

Contract Call

Calling a contract means invoking a function from a smart contract that has been deployed to the blockchain.

Contract ID

The contract ID is a unique identifier for a contract derived from the root of the contract bytecode.

Data Availability

The data availability layer ensures that block data has been published to the network.

EIP

EIP stands for Ethereum Improvement Proposal. It refers to a proposal to upgrade the core software powering the Ethereum blockchain.

EOA

EOA stands for Externally Owned Account. It refers to a wallet address that is not controlled by a contract.

EVM

EVM stands for Ethereum Virtual Machine, which is the virtual machine used for the Ethereum network.

Execution

Execution refers to the processing of transactions by nodes in a network.

Faucet

A faucet is a service that provides free tokens for a testnet.

Forc

Forc is short for Fuel Orchestrator. Similar to Cargo for Rust, Forc is the build system and package manager for Sway. It is used to build, test, and deploy Sway contracts.

Fraud Proof

Fraud proofs are a blockchain verification mechanism whereby a claim on a new block is accepted unless a proof the claim is invalid is provided within some configurable time window. Both the Fuel protocol and the FuelVM are designed to be fraud-provable in restrictive environments such as the Ethereum Virtual Machine.

Fuel

The Fuel blockchain.

Fuels

Fuels is the name of the Fuel Rust and Typescript SDKs used to interact with a contract, similar to ethers.js or web3.js

Fuelup

Fuelup is the official toolchain and package manager for the Fuel toolchain.

FuelVM

The FuelVM is the virtual machine powering the Fuel blockchain.

Fuel Core

fuel-core is the name of the Fuel client implementation.

Gas

Gas is a variable fee charged by a node to process a transaction that is executed on-chain.

Indexer

An indexer is a program that watches and organizes blockchain data so it can be easily queried.

Input

An input refers to a transaction input, which is a UTXO consumed by a transaction.

Layer 1 (L1)

Also called a level 1, this refers to a base layer blockchain that is not built on top of any other blockchain.

Layer 2 (L2)

Also called a level 2, this is a blockchain that is built on top of another blockchain. Layer 2 networks can offer unique benefits like allowing for cheaper transactions or sovereign rollups that can fork without forking the base layer.

Light Client

A light client is a client that doesn't validate blocks and transactions but still offers some functionality to send transactions.

Locked Wallet

A locked wallet is a wallet that can only interact with read-only smart contract methods.

Mainnet

Mainnet refers to the main network of a blockchain, as opposed to a testnet.

Merkle Tree

A Merkle tree is a data structure which uses a cryptographic hash function recursively to condense a set of data into a single value, called the root. It allows efficient proofs that a given element is part of the set.

Message

A type of input that only includes specific metadata, and is often used for bridging.

Mint

Minting refers to the creation of new coins.

Modular

Referring to a blockchain architecture that allows for execution, settlement, consensus, and data availability to run on separate layers.

Monolithic

Referring to a blockchain architecture that handles execution, settlement, consensus, and data availability all at the same time on a single layer.

Native Asset

With Fuel any contract can make its own native asset. On Ethereum, the only native asset is ETH. This allows for much cheaper token transactions because it doesn't require any contract state changes. It also allows you to directly forward any asset in a transaction call, avoiding the need for the approve and transferFrom mechanisms.

Network

Another name for a blockchain.

Node

A client that validates and produces blocks for the network.

Optimistic Rollup

An optimistic rollup is a sidechain that uses fraud proofs to verify transactions instead of relying on a majority of validators to be honest.

Output

An output refers to a transaction output, or which UTXOs are output by a transaction.

Parallel Transactions

Parallel transactions refers to the ability of the FuelVM to process multiple transactions in parallel.

Predicate

A predicate is a pure function that can return true or false, and is sent inside a transaction as bytecode and checked at transaction validity time. If it evaluates to false the transaction will not be processed, and no gas will be used. If it evaluates to true, any coins belonging to the address equal to the Merkle root of the predicate bytecode may be spent by the transaction.

Private Key

A cryptographic key that is used to prove ownership by producing a digital signature. It should be kept private (or secret) as it can grant access to a wallet.

Public Key

A cryptographic key that is generated from its associated private key and can be shared publicly. Addresses are derived from public keys.

Receipt

A receipt is a data object that is emitted during a transaction and contains information about that transaction.

Reentrancy attack

A type of attack in which the attacker is able to recursively call a contract function so that the function is exited before it is fully executed. This can result in the attacker being able to withdraw more funds than intended from a contract.

Rollup

A rollup is a scaling solution for layer 1 blockchains that "rolls up" or batches transactions as calldata.

Script

A script is runnable bytecode that executes once on-chain to perform some task. It does not represent ownership of any resources and it cannot be called by a contract. A script can return a single value of any type.

Settlement

Settlement refers to how and where on-chain disputes are resolved or settled.

Sidechain

A sidechain is a blockchain that runs independently but is connected to another blockchain (often Ethereum Mainnet) by a two-way bridge.

Signature

A cryptographic signature from a wallet, usually in reference to a signature for a message.

Smart Contract

Also referred to as a contract, a smart contract is a set of programming functions with persistent state that is deployed on a blockchain. Once deployed, the contract code can never be changed or deleted, and anyone can access public functions without permission.

State Channel

State channels allow for users to conduct any number of off-chain transactions while only submitting two on-chain transactions to open and close the channel. This reduces the number of on-chain transactions needed, which reduces the cost and saves time.

Sway

Sway is the official programming language for Fuel. It is a domain-specific language crafted for the FuelVM and inspired by Rust.

Testnet

Testnet is short for test network. You can use a testnet to deploy and test contracts for free.

Toolchain

A toolchain is a set of related tools. The Fuel toolchain includes fuelup, forc, fuel-core, and fuels.

Transaction

A transaction is any interaction with the blockchain, for example sending coins from one address to another.

Unlocked Wallet

An unlocked wallet can interact with both read and write smart contract methods.

UTXO

UTXO stands for unspent transaction output.

UTXO Model

A UTXO model is a blockchain model that doesn't keep track of account balances. Instead, it uses inputs and outputs to manage state, which allows for fast parallel transactions.

Validator

A validator refers to a network validator or node. Validators help validate transactions and produce blocks.

Witness

A witness refers to the cryptographic signatures from actors involved in authorizing a transaction, including the transaction signers, contract callers, and block producers.

Zero-Knowledge Proof

A method that allows for the verification of secret information without revealing the secret.

Zero-Knowledge Rollup

A rollup that uses zero-knowledge proofs to verify transactions. In ZK rollups, each rollup block posted to the contract must be accompanied by a validity proof (a succinct proof that the block is valid), which is also verified by the contract. Blocks are thus finalized immediately, and withdrawals can be processed in the same Ethereum block.

Quickstart

Get started with Fuel and discover the path that best suits your needs.

What is Fuel?

Fuel is an operating system purpose built for Ethereum Rollups. Fuel allows rollups to solve for PSI (parallelization, state minimized execution, interoperability) without making any sacrifices.

Here is how we do it:

FuelVM

The FuelVM learns from the Ethereum ecosystem. It implements improvements suggested to the Ethereum VM (EVM) for many years that couldn’t be implemented due to the need to maintain backward compatibility, including parallel transaction execution and multiple native assets.

Fuel delivers unmatched processing capacity through its ability to execute transactions in parallel by using strict state access lists in the form of a UTXO model. With the FuelVM, Fuel full nodes identify the accounts a transaction touches, mapping out dependencies before execution. This enables Fuel to use far more threads and cores of your CPU that are typically idle in single-threaded blockchains. As a result, Fuel can deliver far more compute, state accesses, and transactional throughput than its single-threaded counterparts.

Sway Language

Fuel provides a powerful and sleek developer experience with our own domain-specific language (DSL) called Sway. Sway is based on Rust and includes syntax to leverage a blockchain VM without needlessly verbose boilerplate. Sway was created alongside the FuelVM and designed for the high-compute Fuel environment.

Rust + Solidity = Sway

Sway prioritizes compile-time analysis and safety, similar to Rust’s borrow checker and safety-first semantics. Additionally, it has the syntax of Rust. From Solidity, Sway took the notion of a smart-contract-paradigm language with built-in top-level contract storage and blockchain mechanisms for ergonomic and safe contract programming.

Sway brings the notion of static auditing to smart contracts. In addition, Sway is highly performant and has extensible optimization passes and a modular backend for targeting different blockchain architectures.

<CardSection versionSet={props.versionSet} cardsInfo={[ { link: '/docs/sway', nightlyLink: '/docs/nightly/sway', isExternal: false, heading: 'Sway', headingIcon: 'Code', body: 'Read the official Sway documentation.', }, { link: 'https://sway-playground.org/', isExternal: true, heading: 'Sway Playground', headingIcon: 'Browser', body: 'Get started experimenting with Sway in the browser.', }, { link: 'https://swaybyexample.com/', isExternal: true, heading: 'Sway By Example', headingIcon: 'Beach', body: 'An introduction to Sway with bite-sized simple examples', }, { link: 'https://github.com/FuelLabs/sway-examples', isExternal: true, heading: 'Sway Examples', headingIcon: 'Robot', body: 'Examples of full-stack DeFi applications', }, { link: '/docs/sway-libs', nightlyLink: '/docs/nightly/sway-libs', isExternal: false, heading: 'Sway Libraries', headingIcon: 'Book', body: 'Find useful libraries written in Sway.', }, { link: '/docs/sway-standards', nightlyLink: '/docs/nightly/sway-standards', isExternal: false, heading: 'Sway Standards', headingIcon: 'Book', body: 'Learn about standards for the Sway language', }, { link: 'https://fuellabs.github.io/sway/master/std/', isExternal: true, heading: 'std-lib Reference', headingIcon: 'Book', body: 'Find definitions for helpful types and methods in Sway.', }, { link: 'https://github.com/FuelLabs/sway-applications', isExternal: true, heading: 'Example Applications', headingIcon: 'Apps', body: 'Explore end-to-end applications written in Sway.', }, ]} />

Developer Tooling

Part of what makes Sway so powerful is the fantastic suite of developer tools surrounding it. The Fuel development environment retains the benefits of smart contract languages like Solidity, while adopting the paradigms introduced in the Rust tooling ecosystem.

Now, developers can have a completely vertically integrated experience where every component, from the virtual machine to the CLI, works in harmony.

Sway Tooling

<CardSection versionSet={props.versionSet} cardsInfo={[ { link: '/docs/forc', nightlyLink:'/docs/nightly/forc', isExternal: false, heading: 'Forc', headingIcon: 'Tool', body: 'Explore the Fuel Orchestrator that helps you build, test, and deploy your Sway projects.', }, { link: '/guides/installation/', nightlyLink: '/guides/installation/', isExternal: false, heading: 'Fuelup', headingIcon: 'Settings', body: 'Learn more about the official Fuel toolchain manager that helps install and manage versions.', } ]} />

SDKs & API

<CardSection versionSet={props.versionSet} cardsInfo={[ { link: '/docs/fuels-rs', nightlyLink: '/docs/nightly/fuels-rs', isExternal: false, heading: 'Rust SDK', headingIcon: 'BrandRust', body: 'Test and interact with your Sway program in Rust.', }, { link: '/docs/fuels-ts', nightlyLink: '/docs/nightly/fuels-ts', isExternal: false, heading: 'Typescript SDK', headingIcon: 'BrandTypescript', body: 'Test and interact with your Sway program in TypeScript.', }, { link: '/docs/wallet', nightlyLink: '/docs/nightly/wallet', isExternal: false, heading: 'Wallet SDK', headingIcon: 'Wallet', body: 'Seamlessly integrate a wallet into your application.', }, { link: '/docs/graphql', nightlyLink: '/docs/nightly/graphql', isExternal: false, heading: 'GraphQL API', headingIcon: 'ChartDots3', body: 'Learn about the GraphQL API and interact with the Fuel Network.', }, ]} />

Network

<CardSection versionSet={props.versionSet} cardsInfo={[ { link: /docs/specs, nightlyLink: /docs/nightly/specs, isExternal: false, heading: 'Specs', headingIcon: 'ListDetails', body: 'Explore the specifications for the Fuel Network.', }, { link: props.explorerUrl, isExternal: true, heading: 'Explorer', headingIcon: 'Search', body: 'Explore transactions on the Fuel network.', }, { link: props.bridgeUrl, isExternal: true, heading: 'Bridge', headingIcon: 'BuildingBridge', body: 'Bridge assets to the Fuel network.', }, { link: props.faucetUrl, isExternal: true, heading: 'Faucet', headingIcon: 'Coin', body: Get ${props.fuelTestnet} testnet tokens., } ]} />

Contributing

🌟 Welcome, and thank you for considering contributing to the Fuel docs! 🌟

Before you get started, please take a moment to read through this contributing guide. It contains important information and helpful tips for contributing to the Fuel documentation and guides.

Documentation Content

All documentation, with the exception of the guides and intro section, is pulled in to the docs-hub repo via git submodules.

All contributions to the content of the documentation should be done in the original repository. Below, you will find a list indicating the locations of the source documentation for each book:

Style Guide

See the Style Guide for guidance on how to edit or write documentation.

There are several GitHub actions that run against the docs in each repo to help catch issues. You can learn more about how each of these work in the github-actions repo.

General

You can find the repository for this website on GitHub. For instructions on how to run the repository locally, see the README.

Contribution flow

This is a rough outline of what a contributor's workflow looks like:

  • Create a feature branch off of the master branch, which is typically the base for your work.
  • Make your changes, and commit your work.
  • Run tests and make sure all tests pass.
  • Push your changes to a branch in your fork of the repository and submit a pull request.
    • Use one of the following tags in the title of your PR:
      • feat: - A new feature
      • fix: - A bug fix
      • docs: - Documentation only changes
      • style: - Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
      • refactor: - A code change that neither fixes a bug nor adds a feature
      • perf: - A code change that improves performance
      • test: - Adding missing tests or correcting existing tests
      • build: - Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
      • ci: - Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
      • chore: - Other changes that don't modify src or test files
      • revert: - Reverts a previous commit
  • Complete the contributor agreement on the PR if it is not already completed.
  • Your PR will be reviewed and some changes may be requested.
    • Once you've made the requested changes, your PR must be re-reviewed and approved.
    • If the PR becomes out of date, you can use GitHub's 'update branch' button.
    • If there are conflicts, you can merge and resolve them locally. Then push to your PR branch. Any changes to the branch will require a re-review.
  • GitHub Actions will automatically test all authorized pull requests.
  • Use GitHub to merge the PR once approved.

Linking issues

If the pull request resolves the relevant issues, and you want GitHub to close these issues automatically after it merged into the default branch, you can use the syntax (KEYWORD #ISSUE-NUMBER) like this:

close #123

If the pull request links an issue but does not close it, you can use the keyword ref like this:

ref #456

Multiple issues should use full syntax for each issue and separate by a comma, like:

close #123, ref #456

Reporting Bugs

If you notice any bugs in the live website, please create a new issue on GitHub with:

  • a description of the bug
  • step-by-step instructions for how to reproduce the bug

Understanding the GitHub Workflows

The docs-hub repo uses GitHub Actions to automate the process of updating the documentation, checking for broken links, and more. This page explains the different workflows and how to fix them if they fail.

DocSearch Scrap (docs-scrapper.yml )

This action updates the Algolia search index by scraping the live docs.fuel.network site. It only runs when a Fuel contributor manually runs it.

Guides (guides.yml )

This action runs a spell check on all the guides to catch any mispelled words. It also runs Playwright tests for some of the guides to make sure they work as expected.

The files checked for spelling are configured in .spellcheck.yml. This is also where you can configure what types of elements are ignored.

If the spell check test fails:

  • look up the word in the question to verify it is a real word and is correctly spelled

  • If it is a file name or is code, use backticks to ignore the word.

  • If it is a real word that is spelled correctly, or an acronym that is either common or is defined already, add it to spell-check-custom-words.txt.

  • If possible, rewrite the sentence.

  • If it otherwise should be ignored, you can configure the pipeline in .spellcheck.yml.

To fix a failed guides test, refer to the Guides Testing section.

This workflow tests the links in the docs-hub to make sure none are broken.

PR Checks (pr.yml )

This workflow checks the name of the pull request, checks to make sure there are no dependency vulnerabilities, and runs a lint check for the markdown files and code.

To fix a failing PR title check, change the name of your PR so it uses the convention below:

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

To fix a failing audit check:

pnpm audit --fix
pnpm install

To fix a failing lint check:

pnpm lint:fix:all 

Update Nightly (update-nightly.yml )

This workflow runs every day Monday through Friday to update the nightly version of the documentation. See the Versions section for more information.

Guides

All guides can be found in the docs/guides folder of the docs-hub repo.

The guide content is in the docs/guides/docs folder while the example code is found in the docs/guides/examples folder.

Note that the some content is pulled in from submodules. To make any changes to the content or code from a submodule, you must make a pull request on the source repo.

Style Guide

See the Style Guide for guidance on how to edit or write guides.

Testing

Some guides are tested with a GitHub workflow that runs on pull requests. If you are creating a new guide, it is recommended you create a test using the TestAction component.

You can run the tests locally with the command below:

pnpm test:guides

Here is how it works:

Within the guide markdown files, there are TestAction components that are used run a Playwright test. The test follows each step in the guide based on these components and checks to see if everything works as expected. You can find the test files inside the tests folder.

The TestAction component accepts two props:

The id must be a unique string id. The action prop contains information about the action to run the in test.

You can find examples for how to use this component in the docs/guides/docs folder, you can find all of the action options in tests/utils/types.ts.

Refer to the Playwright docs for information on locators and selecting elements in a test.

Style Guide

This style guide is a set of guidelines for writing and editing documentation. It's important to follow these guidelines to ensure that our documentation is consistent and easy to read.

General Guidelines

Writing

  • Use a friendly and conversational tone of voice.
  • Use an active voice (vs. a passive voice).
  • Avoid long paragraphs or sentences.
  • Always use accurate and verified information.
  • Maintain consistency in style across pages and sections.
  • Assume the reader does not have a lot of context. Keep in mind that readers have different levels of expertise.
  • Don't use click here or read this document for links. Just link the thing in context.
  • Don’t use double negatives.

Words

  • Use second-person perspective (use you).
  • Use American English.
  • Use simple (but accurate) words.
  • Avoid slang, jargon, or making up new words. Everything should be easy to translate into major languages.
  • Avoid gendered words or pronouns like “his”, “her”, “manned”, etc.
  • Define acronyms and abbreviations on first usage and if they're used infrequently.
  • Use italics or bold text to emphasize a word. Avoid using all capital letters.
  • Avoid using words that indicate time like “new feature”, as it may fall out-of-date quickly.
  • Avoid the word “please” in an instruction.
  • Avoid violent words.
  • Don’t use offensive language.

Code

  • Use code examples whenever possible.
  • Always specify the language of a code block.
  • Avoid hard-coded examples, and instead import code examples from code that is routinely tested.
  • Use comments to define code examples to be imported instead of line numbers that may change.
  • Always wrap inline code in backticks.
  • Always use code fences when showing commands and separate commands from console outputs. The user should be able to copy and paste the entire code in the code fence.
  • Use descriptive variable names in code examples. Don’t use foo , bar , baz , etc.

Organizing Information

  • Use the standard HTML heading hierarchy: The first line should be an h1 (use 1 # in markdown) and should be the only h1 on the page. The subheadings shouldn’t skip a level, e.g. h3 tags should only be inside h2 tags.
  • Organize information so that readers can skim the page and get an answer for the most common questions quickly. Use subheadings to call out important information, and use a blockquote to identify supplemental information.
  • Avoid using tables.

Graphics

  • Don’t create complex flow-charts (having more than 5-6 items).
  • Don't use images of text or code. Use the actual text or code in markdown format.

Lists

  • Use numbers, number-letter combinations (1.a, etc.), or bullet points for lists. Do not use Roman numerals or letters alone.

Guides

If you are writing for a guide or the intro section, follow these additional guidelines:

Components

To maintain accuracy and consistency, it is recommended to use the available React components within a guide whenever they apply. For example:

  • Use the CodeImport and TextImport components instead of copying and pasting code or text.
  • For images, use the Box.Centered component to center the image.
  • For content only applicable to a certain version of the docs, use the ConditionalContent component.

You can find examples for how to these components within the docs/guides/docs folder.

For a full list of components available, see the src/components/MDXRender.tsx component.

Variables

There are several variables passed into the MDX context that you can use within a guide. You can find a full list in the src/components/MDXRender.tsx component.

You can then use these variables within a guide like so:

The faucet URL is {props.faucetUrl}

Which would render as: The faucet URL is {props.faucetUrl}

Versions

There are two version sets of the docs available in the docs-hub: testnet(the default version), and nightly.

  • The default version set is compatible with the latest toolchain and testnet.
  • The nightly version set reflects the latest releases on GitHub. These versions may not be compatible with each other.

Updating the Nightly Versions

There is a Github action that runs every Monday and Thursday at 12:00 UTC to update the nightly versions of the docs.

You can also update the nightly versions locally by running the command below:

pnpm docs:update:nightly

The nightly versions should be kept at the latest release on GitHub for each tool.

Updating the Default Versions

To change the default versions, update the src/config/versions.json file and run this command:

pnpm docs:update

This command will both update the the default versions to match the configuration file and make sure the nightly versions are updated.

Here is how to decide what default versions to use:

  • The Sway & forc versions should match what is on the latest toolchain.
  • The version of the wallet SDK should match the version of the Fuel Wallet extension if it is compatible with the latest toolchain. If the extension is not yet compatible, use the latest release that is compatible.
  • The version of the Rust SDK should be the latest release that is compatible with the default version of forc.
  • The version of the TypeScript SDK should be the latest release that is compatible with the default version of forc and the Fuel wallet.
  • The version of the GraphQL API and Specs books should reflect the version of fuel-core used in the latest testnet. These books currently do not have regular releases.

Guide

Getting Started

Welcome to fuels Typescript SDK!

We prepared some guides to walk you through your first steps:

  1. Installation
  2. Connecting to the Network
  3. Running a local Fuel node
  4. React Example
  5. CDN Usage
  6. Next Steps

Installation

You must install the Fuel Toolchain before using this library.

Then add the fuels dependency to your project:

::: code-group

bun add fuels@{{fuels}}
pnpm add fuels@{{fuels}}
npm install fuels@{{fuels}} --save

:::

Now you are ready to:

Connecting to the Network

After installing the fuels package, it's easy to connect to the Network:

<<< @./snippets/connecting-to-the-network.ts#main{ts:line-numbers}

RPC URLs

These are our official RPC URLs:

NetworkURL
Mainnethttps://testnet.fuel.network/v1/graphql
Testnethttps://mainnet.fuel.network/v1/graphql
LocalhostRunning a local Fuel node

Resources

Access all our apps directly:

MainnetTestnet
Faucethttps://faucet-testnet.fuel.network/
Explorerhttps://app.fuel.networkhttps://app-testnet.fuel.network
Bridgehttps://app.fuel.network/bridgehttps://app-testnet.fuel.network/bridge
GraphQLhttps://mainnet.fuel.network/v1/playgroundhttps://testnet.fuel.network/v1/playground

Running a local Fuel node

Remember to first install the Fuel Toolchain.

Check the online docs:

CommandDocumentation
Fuel Binaryfuel-coredocs — Running a local node
TS SDKfuels nodedocs — Using the fuels CLI

Utilities

Testing

You can run a Fuel node from within your .ts unit tests:

Developing

Configure your project for the fuels CLI using a fuels.config.ts file:

It makes development easier with a hot-reload experience:

More

React Example

import { BN, Provider, Wallet } from "fuels";
import { useEffect, useState } from "react";

function App() {
  const [balance, setBalance] = useState(0);

  useEffect(() => {
    const onPageLoad = async () => {
      const provider = new Provider("https://mainnet.fuel.network/v1/graphql");

      const wallet = Wallet.fromAddress("0x...", provider);
      const { balances } = await wallet.getBalances();

      setBalance(new BN(balances[0].amount).toNumber());
    };

    onPageLoad();
  }, []);

  return <div>My Balance: {balance}</div>;
}

export default App;

See Also

CDN Usage (browser only)

<script type="module">
  import {
    Wallet,
    Provider,
  } from "https://cdnjs.cloudflare.com/ajax/libs/fuels/{{fuels}}/browser.mjs";

  const main = async () => {
    const provider = new Provider(
      "https://mainnet.fuel.network/v1/graphql",
    );
    const { name } = await provider.getChain();
    console.log(name);
  };

  main();
</script>

More

Done!

Wait no more, let's build your first Fuel dApp!

Further Resources

For a more in-depth, step-by-step guide on working with the wider Fuel ecosystem, check out the Developer Quickstart, which uses a more procedural and detailed approach:

  1. Installing all tools needed to develop with the Fuel ecosystem
  2. Writing your first Sway Project
  3. Deploying your contract
  4. Building a simple front-end dApp to interact with your deployed contract

The UTXO Model

In UTXO (Unspent Transaction Output) based systems, each coin is unique, similar to how physical currency bills have different denominations.

A UTXO represents a coin with a specific amount, similar to having a $10 or $5 bill. It's crucial to understand this unique feature of UTXOs, as it differs significantly from Ethereum's account-based system.

In Ethereum, balances are tracked as cumulative totals, similar to a bank account, rather than as distinct 'coins' or 'bills'.

Why UTXOs Matter

Each UTXO corresponds to a unique coin and has an associated amount. This model allows for greater transparency and control in cryptocurrency transactions. Understanding UTXOs is key for effectively managing and tracking your digital assets.

How UTXOs Work

When you create a transaction, you will use UTXOs from your account to fund it. Here's a step-by-step explanation:

1. Selecting UTXOs: The SDK selects one or more UTXOs from your account that together are equal to or greater than the transaction amount plus the transaction fee.

2. Spending UTXOs: These selected UTXOs are used to fund the transaction and cover the transaction fee. For example, if you need to send $15 and the transaction fee is $1, and you have $10 and $6 UTXOs, both will be used.

3. New UTXOs: If the total value of the selected UTXOs exceeds the transaction amount plus the transaction fee, the difference is returned to your account as new UTXOs. For instance, if you spend a $20 UTXO for a $15 transaction with a $1 fee, a new UTXO worth $4 will be created as change and added back to your account.

In summary, the original UTXOs used in the transaction are marked as spent and cannot be used again. The new UTXOs are available for future transactions.

Suppose you have the following UTXOs in your account:

  • $10 UTXO
  • $20 UTXO

You want to send $15 to someone, and the transaction fee is $1. Here's what happens:

  • The $20 UTXO is selected to fund the $15 transaction and cover the $1 fee.
  • The transaction is completed and the $20 UTXO is spent.
  • A new $15 UTXO is generated to the recipient, and a new $4 UTXO (change) is created and added to your account.

Creating a Fuel dApp

npm create fuels is a command line tool that helps you scaffold a new full-stack Fuel dApp. In this guide, we will create a new counter dApp using npm create fuels and add decrement functionality to it. The final result will look like this:

End result of this guide

You can also check it live, deployed to the Testnet:

Initializing the project

The first step is to run the command:

::: code-group

npm create fuels@{{fuels}}
pnpm create fuels@{{fuels}}
bun create fuels@{{fuels}}

:::

Once you run the command, you will be asked to choose a name for your project:

◇ What is the name of your project?
│ my-fuel-project
└

The tool will scaffold the project and install the necessary dependencies for you. You will then be greeted with this message:

⚡️ Success! Created a fullstack Fuel dapp at my-fuel-project

To get started:

- cd into the project directory: cd my-fuel-project
- Start a local Fuel dev server: pnpm fuels:dev
- Run the frontend: pnpm dev

-> TS SDK docs: https://docs.fuel.network/docs/fuels-ts/
-> Sway docs: https://docs.fuel.network/docs/sway/
-> If you have any questions, check the Fuel forum: https://forum.fuel.network/

Directory Structure

The project scaffolded by npm create fuels has roughly the following directory structure:

my-fuel-project
├── src
│ ├── components
│ │ └── ...
│ ├── hooks
│ │ └── ...
│ ├── lib.tsx
│ ├── App.tsx
│ └── ...
├── sway-programs
│ ├── contract
│ │ └── ...
│ └── ...
├── public
│ └── ...
├── fuels.config.ts
├── package.json
└── ...

It is a Vite project with a few extra files and folders. Let's take a closer look at some of the important ones:

./fuels.config.ts

This is the configuration file for the fuels CLI, the CLI and tooling that powers this project under the hood. It makes sure that all of your Sway programs are continuously compiled and deployed to your local Fuel node. You can read more about the fuels.config.ts file in the Fuels CLI documentation.

./sway-programs/contract/src/main.sw

This is where our Sway contract lives. Out of the box, it is a simple counter contract that can only be incremented. We will add a decrement functionality to it in the next step.

./src/App.tsx

This file contains the source code for the frontend of our dApp.

./src/components/Contract.tsx

This file contains the source code for the 'Contract' tab in the UI, this is where the contract calling logic is implemented.

Dev Environment Setup

Now that we have our project scaffolded, let's set up our development environment.

Let's first start our Fuel Dev server. This will start a local Fuel node and continuously compile and deploy our Sway programs to it.

::: code-group

npm fuels:dev
pnpm fuels:dev
bun run fuels:dev

:::

Once the server is up and running, we can start our Next.js development server in another terminal.

::: code-group

pnpm dev
pnpm dev
bun run dev

:::

You should now be able to see the dApp running at http://localhost:5173. Go ahead and connect a wallet to the dApp. You can choose the Burner Wallet from the list if you don't want to connect a wallet.

Available Wallet Connectors

Now, you can try changing the contents of the ./sway-programs/contract/src/main.sw file and see the changes reflected in the 'Contract' tab in the UI without having to restart the server.

Fullstack Fuel Dev Workflow

Note: You may wish to learn more about how you could create a Fuel dApp that uses predicates, check out our Working with Predicates guide.

Adding Decrement Functionality

To add decrement functionality to our counter, we will have to do two things: 1. Add a decrement_counter function to our Sway contract, and 2. Modify the ./src/components/Contract.tsx file to add a button that calls this function.

1. Modifying the Sway Contract

To add a decrement_counter function to our Sway contract, we will modify the ./sway-programs/contract/src/main.sw file.

There are two steps when adding a new function to a Sway program. The first step is to specify the function's ABI.

Towards the top of the file, you will find the ABI section for the contract. Let's add a new function to it:

<<< @/../../create-fuels-counter-guide/sway-programs/contract/src/main.sw#create-fuels-counter-guide-abi{rust:line-numbers}

The second step is to implement the function.

We will add the implementation of the decrement_counter function right below the increment_counter function.

<<< @/../../create-fuels-counter-guide/sway-programs/contract/src/main.sw#create-fuels-counter-guide-impl{rust:line-numbers}

2. Modifying the Frontend

We will now add a new button to the frontend that will call the decrement_counter function when clicked. To do this, we will modify the ./src/App.tsx file.

First, we will add a function called decrementCounter similar to the incrementCounter function:

<<< @/../../create-fuels-counter-guide/src/components/Contract.tsx#create-fuels-counter-guide-on-decrement-react-function{ts:line-numbers}

Second, we will add a new button to the UI that will call the decrementCounter function when clicked:

<Button onClick={onDecrementPressed} className="mt-6">
  Decrement Counter
</Button>

Congratulations! You should now be able to see the counter dApp running at http://localhost:5173 with our newly added decrement functionality.

You can find the complete source code of the dApp we built here.

End result of this guide

Whenever you want to add a new feature to your dApp and quickly prototype things, you can follow the same steps we followed in this guide.

3. Extending the contract testing suite (Optional)

Testing our smart contract is a good practice to ensure that our implementation is working as expected. It also give assurances down the line if we decide to change the implementation of our contract.

We write our test in the #[test] macro within our Sway contract, these can be inline within our Sway contract or in a separate file.

For the guide, we'll add a test for our new decrement_counter function in the ./sway-programs/contract/src/main.sw file:

<<< @/../../create-fuels-counter-guide/sway-programs/contract/src/main.sw#create-fuels-counter-guide-sway-contract-test{rust:line-numbers}

After writing our test, we can run either using forc test or via PNPM using pnpm test:forc.

4. Extending the integration test suite (Optional)

Testing the integration with your smart contract isn't essential, but it's good practice to ensure that your application is working as expected. It also gives you the ability to test your application in a controlled environment against a local node.

We've provided some examples for each program type in the ./test directory of your project. But let's also add a test for our new decrement_counter function in the ./test/contract.test.ts file:

<<< @./snippets/decrement-counter.ts#full{ts:line-numbers}

The template also comes with a UI testing setup using Playwright. We can add a test for our new decrement_counter function in the ./test/ui/ui.test.ts file:

<<< @/../../create-fuels-counter-guide/test/ui/ui.test.ts#decrement-counter-ui-test{ts:line-numbers}

Next Steps

  • Now that you have a basic counter dApp running and have the npm create fuels workflow powering you, you can start building more complex dApps using the Fuel Stack. A good place to start for ideas and reference code is the Sway Applications Repo.

  • As you may have noticed, there are different types of programs in your dApp, feel free to explore Predicates and Scripts, which are both important differentiators in the Fuel Stack.

  • If you want to deploy your dApp to the testnet, check out our Deploying a dApp to Testnet guide.

  • If you want to further validate the functionality of your dApp and program types, check out the test directory in your create fuels project. Couple this with our testing guide to get a better understanding of how to test your dApp.

  • If you have any questions or need help, feel free to reach out to us on the Official Fuel Forum.

  • If you want to learn more about the Fuel Stack, check out the Fuel Docs.

Options

The npm create fuels command has several command-line options that you can use to customize your project.

::: code-group

pnpm create fuels@{{fuels}} [project-name] [options]
npm create fuels@{{fuels}} -- [project-name] [options]
bun create fuels@{{fuels}} [project-name] [options]

:::

--template <template-name>

Specifies the template to use for your project. The available templates are: vite and nextjs. The default template is vite.

--verbose

Enables verbose logging. Useful when debugging issues with the tool.

-h, --help

Displays a help message with all available options.

-V, --version

Displays the version number of the npm create fuels command.

Deploying a dApp to Testnet

In this guide, we will deploy a full-stack dApp bootstrapped with npm create fuels to the Fuel testnet.

Make sure you have already bootstrapped a dApp using npm create fuels. If you haven't, please follow this guide.

There are mainly two steps to get our dApp live on the testnet:

  1. Deploying the Contract to the Testnet
  2. Deploying the Frontend to the Cloud

Deploying the Contract

We will be using forc to deploy our contracts to the testnet. forc is a part of the Fuel Toolchain.

If you don't have the Fuel Toolchain installed, follow this guide to install it.

The first step is to cd into the directory containing your contract:

cd sway-programs/contract

And then, run the following command and follow the instructions to deploy the contract to the testnet:

forc deploy --testnet

You can check out this guide for more information on deploying a contract to the testnet.

You should see a message similar to this:

Contract deploy-to-testnet Deployed!

Network: https://testnet.fuel.network
Contract ID: 0x8342d413de2a678245d9ee39f020795800c7e6a4ac5ff7daae275f533dc05e08
Deployed in block 0x4ea52b6652836c499e44b7e42f7c22d1ed1f03cf90a1d94cd0113b9023dfa636

Copy the contract ID and save it for later use.

Deploying the Frontend

Let's now prepare our frontend so that we can deploy it to the cloud.

Go to your .env.local file and add a new variable named VITE_TESTNET_CONTRACT_ID. Set its value to the contract ID you had copied earlier after deploying your contract.

VITE_TESTNET_CONTRACT_ID=0x8342d413de2a678245d9ee39f020795800c7e6a4ac5ff7daae275f533dc05e08

If you are curious, this environment variable is used here in the src/lib.tsx file to set the contract ID:

<<< @/../../create-fuels-counter-guide/src/lib.tsx#deploying-dapp-to-testnet-frontend-contract-id{ts:line-numbers}

You will notice that this piece of code is getting the contract ID depending on the current environment. If the environment is local, it will use the contract ID from the auto-generated contract-ids.json file. Otherwise, for a testnet deployment, it will use the contract ID provided by you.

The CURRENT_ENVIRONMENT variable is defined in the lib.tsx file:

<<< @/../../create-fuels-counter-guide/src/lib.tsx#deploying-dapp-to-testnet-lib-current-environment{ts:line-numbers}

As you can see, it depends on the VITE_DAPP_ENVIRONMENT environment variable. If you go to your .env.local file, you will see that it is set to local by default. If you change this value to testnet, the frontend will now be connected to the testnet instead of your local node.

Go ahead and change the VITE_DAPP_ENVIRONMENT value to testnet in your .env.local file. If you run your frontend now, you should be able to interact with your contract on the testnet.

To deploy your frontend to the cloud, you can use any service like Vercel. Make sure that you setup your environment variables correctly and that your contract ID is correct. Your environment variables should look something like this:

VITE_DAPP_ENVIRONMENT=testnet
VITE_TESTNET_CONTRACT_ID=0x8342d413de2a678245d9ee39f020795800c7e6a4ac5ff7daae275f533dc05e08

(the rest of the environment variables are optional)

Conclusion

Congratulations! You have successfully deployed your Fuel dApp to the testnet.

To recap, to deploy your dApp to the testnet, you need to:

  1. Deploy your contract to the testnet using forc deploy --testnet.
  2. Specify this contract ID in your frontend's environment variables. (VITE_TESTNET_CONTRACT_ID)
  3. Set the VITE_DAPP_ENVIRONMENT environment variable to testnet.

Working with Predicates

This guide builds on the Creating a Fuel dApp guide. Once you've gotten the dApp there up and running, then you can continue here via clicking the Predicate Example link. We will modify the predicate we created in the previous guide. The final result will look like this:

End result of this guide

You can also check it live, deployed to the Testnet:

Adding a Configurable pin

The current predicate functionality we have is a simple one that checks if the user has a pin. We will modify this predicate to accept a configurable pin. This will allow the user to set their own pin.

  1. Modifying the Predicate Contract

The first step is to modify the predicate contract to accept a configurable pin. We will use the configurable keyword to create an updatable constant to store the pin. We will also modify the main function to check this constant instead of a hardcoded pin.

<<< @/../../docs/sway/configurable-pin/src/main.sw#full{rust:line-numbers}

  1. Modifying the Frontend

We will now add new button to the frontend that will update the pin in the predicate when clicked. To do this, we will modify the ./src/components/Predicate.tsx file.

We will add a function called changePin, which will use the current pin in state to update the pin in the predicate as well as transfer 1000 to the predicate.

<<< @/../../create-fuels-counter-guide/src/components/Predicate.tsx#change-pin-react-function{ts:line-numbers}

It would also be useful to change the placeholder text.

<input
  type="text"
  value={predicatePin}
  onChange={(e) => setPredicatePin(e.target.value)}
  className="w-1/2 bg-gray-800 rounded-md px-2 py-1 mr-3 truncate font-mono"
  placeholder="Enter current or new pin"
/>

Finally, we will add a button that calls the changePin function when clicked.

<Button onClick={changePin} className="w-full" disabled={isLoading}>
  Change Pin
</Button>

Congratulations! That's all. You should now be able to see the modified predicate dApp running at http://localhost:5173 with our newly added change pin functionality.

You can find the complete source code of the dApp we built here.

Next Steps

  • Now that you have a predicate dApp running and have the npm create fuels workflow powering you, you can start building more complex dApps using the Fuel Stack. A good place to start for ideas and reference code is the Sway Applications Repo.

  • If you have any questions or need help, feel free to reach out to us on the Official Fuel Forum.

  • If you want to learn more about the Fuel Stack, check out the Fuel Docs.

Fuels CLI

The quickest way to build full stack Fuel dApps.

  • fuels init — Creates a new fuels.config.ts file
  • fuels build — Build forc workspace and generate Typescript types for everything
  • fuels deploy — Deploy workspace contracts and save their IDs to JSON file
  • fuels dev — Start local Fuel Core node and build + deploy on every file change

Getting started

Imagine you have this file structure:

my-fuel-dapp # NextJS app or similar
├── sway-programs # Forc's workspace
│   ├── src
│   ├── ...
│   └── Forc.toml
├── public
│   └── ...
├── src
│   ├── app
│   ├── ...
├   └── sway-programs-api # Type-safe generated API
└── package.json

Prerequisites

The Fuel Toolchain and its components (namely forc and fuel-core) are pre-requisite for several operations with the Fuels CLI. For example:

  • Building out contracts using fuels build requires forc.
  • Deploying contracts locally using fuels deploy requires fuel-core.

Follow the installation guide if you don't have them installed already.

Installation

Add it to your my-fuel-dapp project:

::: code-group

npm install fuels@{{fuels}} --save
pnpm add fuels@{{fuels}}
bun add fuels@{{fuels}}

:::

Double-checking

npx fuels@{{fuels}} -v

Next Step

Use fuels init to create a fuel.config.ts file.

Config File

Here, you can learn more about all configuration options.

workspace

Relative directory path to Forc workspace.

<<< @/../../demo-fuels/fuels.config.full.ts#workspace{ts:line-numbers}

The property workspace is incompatible with contracts, predicates, and scripts.

contracts

List of relative directory paths to Sway contracts.

<<< @/../../demo-fuels/fuels.config.full.ts#contracts{ts:line-numbers}

The property contracts is incompatible with workspace.

predicates

List of relative directory paths to Sway predicates.

<<< @/../../demo-fuels/fuels.config.full.ts#predicates{ts:line-numbers}

The property predicates is incompatible with workspace.

scripts

List of relative directory paths to Sway scripts.

<<< @/../../demo-fuels/fuels.config.full.ts#scripts{ts:line-numbers}

The property scripts is incompatible with workspace.

output

Relative directory path to use when generating Typescript definitions.

<<< @/../../demo-fuels/fuels.config.full.ts#output{ts:line-numbers}

providerUrl

The URL to use when deploying contracts.

<<< @/../../demo-fuels/fuels.config.full.ts#providerUrl{ts:line-numbers}

When autostartFuelCore property is set to true, the providedUrl is overridden by that of the local short-lived fuel-core node started by the fuels dev command.

privateKey

Wallet private key, used when deploying contracts.

This property should ideally come from env — process.env.MY_PRIVATE_KEY.

<<< @/../../demo-fuels/fuels.config.full.ts#privateKey{ts:line-numbers}

When autostartFuelCore property is set to true, the privateKey is overridden with the consensusKey of the local short-lived fuel-core node started by the fuels dev command.

snapshotDir

Relative path to directory containing custom configurations for fuel-core, such as:

  • chainConfig.json
  • metadata.json
  • stateConfig.json

This will take effect only when autoStartFuelCore is true.

<<< @/../../demo-fuels/fuels.config.full.ts#snapshotDir{ts:line-numbers}

autoStartFuelCore

When set to true, it will automatically:

  1. Starts a short-lived fuel-core node as part of the fuels dev command
  2. Override property providerUrl with the URL for the recently started fuel-core node

<<< @/../../demo-fuels/fuels.config.full.ts#autoStartFuelCore{ts:line-numbers}

If set to false, you must spin up a fuel-core node by yourself and set the URL for it via providerUrl.

fuelCorePort

Port to use when starting a local fuel-core node.

<<< @/../../demo-fuels/fuels.config.full.ts#fuelCorePort{ts:line-numbers}

forcBuildFlags

Sway programs are compiled in debug mode by default.

Here you can customize all build flags, e.g. to build programs in release mode.

<<< @/../../demo-fuels/fuels.config.full.ts#forcBuildFlags{ts:line-numbers}

Check also:

deployConfig

You can supply a ready-to-go deploy configuration object:

<<< @/../../demo-fuels/fuels.config.full.ts#deployConfig-obj{ts:line-numbers}

Or use a function for crafting dynamic deployment flows:

  • If you need to fetch and use configs or data from a remote data source
  • If you need to use IDs from already deployed contracts — in this case, we can use the options.contracts property to get the necessary contract ID. For example:

<<< @/../../demo-fuels/fuels.config.full.ts#deployConfig-fn{ts:line-numbers}

onBuild

A callback function that is called after a build event has been successful.

Parameters:

  • config — The loaded config (fuels.config.ts)

<<< @/../../demo-fuels/fuels.config.full.ts#onBuild{ts:line-numbers}

onDeploy

A callback function that is called after a deployment event has been successful.

Parameters:

  • config — The loaded config (fuels.config.ts)
  • data — The data (an array of deployed contracts)

<<< @/../../demo-fuels/fuels.config.full.ts#onDeploy{ts:line-numbers}

onDev

A callback function that is called after the fuels dev command has successfully restarted.

Parameters:

  • config — The loaded config (fuels.config.ts)

<<< @/../../demo-fuels/fuels.config.full.ts#onDev{ts:line-numbers}

onNode

A callback function that is called after the fuels node command has successfully refreshed.

Parameters:

  • config — The loaded config (fuels.config.ts)

<<< @/../../demo-fuels/fuels.config.full.ts#onNode{ts:line-numbers}

onFailure

Pass a callback function to be called in case of errors.

Parameters:

  • config — The loaded config (fuels.config.ts)
  • error — Original error object

<<< @/../../demo-fuels/fuels.config.full.ts#onFailure{ts:line-numbers}

forcPath

Path to the forc binary.

When not supplied, will default to using the system binaries (forc).

<<< @/../../demo-fuels/fuels.config.full.ts#forcPath{ts:line-numbers}

fuelCorePath

Path to the fuel-core binary.

When not supplied, will default to using the system binaries (fuel-core).

<<< @/../../demo-fuels/fuels.config.full.ts#fuelCorePath{ts:line-numbers}

Loading environment variables

If you want to load environment variables from a .env file, you can use the dotenv package.

First, install it:

::: code-group

pnpm install dotenv
npm install dotenv
bun install dotenv

:::

Then, you can use it in your fuels.config.ts file:

<<< @/../../create-fuels-counter-guide/fuels.config.ts#fuels-config-file-env{ts:line-numbers}

Commands

The fuels CLI consists of a couple of commands.

fuels init

npx fuels@{{fuels}} help init
Options:
  --path <path>                Path to project root (default: current directory)
  -w, --workspace <path>       Relative dir path to Forc workspace
  -c, --contracts [paths...]   Relative paths to Contracts
  -s, --scripts [paths...]     Relative paths to Scripts
  -p, --predicates [paths...]  Relative paths to Predicates
  -o, --output <path>          Relative dir path for Typescript generation output
  --forc-path <path>           Path to the `forc` binary
  --fuel-core-path <path>      Path to the `fuel-core` binary
  --auto-start-fuel-core       Auto-starts a `fuel-core` node during `dev` command
  --fuel-core-port <port>      Port to use when starting a local `fuel-core` node for dev mode
  -h, --help                   Display help

Creating a sample fuel.config.ts file:

npx fuels@{{fuels}} init --contracts ./my-contracts/* --output ./src/sway-contracts-api

Using Forc workspaces? Try this instead:

npx fuels@{{fuels}} init --workspace ./sway-programs --output ./src/sway-programs-api

This will give you a minimal configuration:

<<< @/../../demo-fuels/fuels.config.minimal.ts#config{ts:line-numbers}

In a nutshell:

.
├── sway-programs # <— forc workspace
├── src
│   └── sway-programs-api # <— output
├── fuels.config.ts
└── package.json

See more

fuels build

npx fuels@{{fuels}} help build
Options:
  --path <path>  Path to project root (default: "/Users/anderson/Code/fuel/fuels-ts/apps/docs")
  -d, --deploy       Deploy contracts after build (auto-starts a `fuel-core` node if needed)
  -h, --help         Display help

Examples:

npx fuels@{{fuels}} build
  1. Build all Sway programs under your workspace using forc 1
  2. Generate types for them using fuels-typegen 2
npx fuels@{{fuels}} build --deploy

Using the --deploy flag will additionally:

  1. Auto-start a short-lived fuel-core node if needed (docs)
  2. Run deploy on that node

This is useful when working with contracts because a contract's ID is generated only on deployment.

fuels deploy

npx fuels@{{fuels}} deploy

The fuels deploy command does two things:

  1. Deploy all Sway contracts under workspace.
  2. Saves their deployed IDs to:
    • ./src/sway-programs-api/contract-ids.json
{
  "myContract1": "0x..",
  "myContract2": "0x.."
}

Use it when instantiating your contracts:

<<< @/../../demo-fuels/src/index.test.ts#using-generated-files{ts:line-numbers}

For a complete example, see:

Proxy Contracts Deployment

Automatic deployment of proxy contracts can be enabled in Forc.toml.

For more info, please check these docs:

fuels dev

npx fuels@{{fuels}} dev

The fuels dev command does three things:

  1. Auto-start a short-lived fuel-core node (docs)
  2. Runs build and deploy once at the start
  3. Watches your Forc workspace and repeats the previous step on every change

In dev mode, every time you update a contract on your Forc workspace, we re-generate type definitions and factory classes for it, following your pre-configured output directory. If it's part of another build system running in dev mode (i.e. next dev), you can expect it to re-build / auto-reload as well.

fuels node

npx fuels@{{fuels}} node

Starts a short-lived fuel-core node and requires a fuels.config.ts config file.

Generate one with fuels init:

<<< @/../../demo-fuels/fuels.config.minimal.ts#config{ts:line-numbers}

fuels typegen

Manually generates type definitions and factory classes from ABI JSON files.

npx fuels@{{fuels}} help typegen
Options:
  -i, --inputs <path|glob...>  Input paths/globals to your Abi JSON files
  -o, --output <dir>           Directory path for generated files
  -c, --contract               Generate types for Contracts [default]
  -s, --script                 Generate types for Scripts
  -p, --predicate              Generate types for Predicates
  -S, --silent                 Omit output messages

For more info, check:

fuels versions

Check for version incompatibilities between your Fuel Toolchain component versions, matching them against the ones supported by the Typescript SDK version that you have.

npx fuels@{{fuels}} versions
┌───────────┬───────────┬────────────────┬─────────────┐
│           │ Supported │ Yours / System │ System Path │
├───────────┼───────────┼────────────────┼─────────────┤
│ Forc      │ {{forc}}    │ {{forc}}         │ forc        │
├───────────┼───────────┼────────────────┼─────────────┤
│ Fuel-Core │ {{fuelCore}}    │ {{fuelCore}}         │ fuel-core   │
└───────────┴───────────┴────────────────┴─────────────┘

You have all the right versions! ⚡

ABI Typegen

The JSON ABI file

Whether you want to deploy or connect to a pre-existing smart contract, the JSON ABI file is what makes it possible.

It tells the SDK about the ABI methods in your Smart Contracts and Scripts

Given the following Sway smart contract:

#![allow(unused)]
fn main() {
contract;

abi MyContract {
    fn test_function() -> bool;
}

impl MyContract for Contract {
    fn test_function() -> bool {
        true
    }
}
}

The JSON ABI file would look something like this:

$ cat out/debug/my-test-abi.json
[
  {
    "type": "function",
    "inputs": [],
    "name": "test_function",
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "components": null
      }
    ]
  }
]

See also:

Generating Types from ABI

Installation

First we install fuels to our project:

pnpm add fuels@{{fuels}}

Help

A first glance at the docs:

$ pnpm fuels typegen -h

Usage: fuels typegen [options]

Generate Typescript from Sway ABI JSON files

Options:
  -i, --inputs <path|glob...>  Input paths/globals to your ABI JSON files
  -o, --output <dir>           Directory path for generated files
  -c, --contract               Generate types for Contracts [default]
  -s, --script                 Generate types for Scripts
  -p, --predicate              Generate types for Predicates
  -S, --silent                 Omit output messages
  -h, --help                   Display help

Generating Types for Contracts

You can generate types for a Sway contract using the command below:

pnpm fuels typegen -i ./abis/*-abi.json -o ./types

The path after the input flag -i should point to the file ending in -abi.json produced when the contract was built.

The path after the output flag -o will be the output directory for the generated types.

You can omit the --contract option here since it's the default.

Generating Types for Scripts

To generate types for a Sway script, use the --script flag:

pnpm fuels typegen -i ./abis/*-abi.json -o ./types --script

Generating Types for Predicates

To generate types for a Sway predicate, use the --predicate flag:

pnpm fuels typegen -i ./abis/*-abi.json -o ./types --predicate

See also:

Using Generated Types

After generating types via:

pnpm fuels typegen -i ./abis/*-abi.json -o ./types

We can use these files like so:

<<< @/../../demo-typegen/src/demo.test.ts#typegen-demo-contract-factory-connect{ts:line-numbers}

Contract

Let's use the Contract class to deploy a contract:

<<< @/../../demo-typegen/src/demo.test.ts#typegen-demo-contract-factory-deploy{ts:line-numbers}

Autoloading of Storage Slots

Typegen tries to resolve, auto-load, and embed the Storage Slots for your Contract within the MyContract class. Still, you can override it alongside other options from DeployContractOptions, when calling the deploy method:

<<< @/../../demo-typegen/src/demo.test.ts#typegen-demo-contract-storage-slots{ts:line-numbers}

Script

After generating types via:

pnpm fuels typegen -i ./abis/*-abi.json -o ./types --script

We can use these files like so:

<<< @/../../demo-typegen/src/demo.test.ts#typegen-demo-script{ts:line-numbers}

Predicate

After generating types via:

pnpm fuels typegen -i ./abis/*-abi.json -o ./types --predicate

We can use these files like so:

<<< @/../../demo-typegen/src/demo.test.ts#typegen-demo-predicate{ts:line-numbers}

See also:

Provider

The Provider lets you connect to a Fuel node (docs) and interact with it, encapsulating common client operations in the SDK. Those operations include querying the blockchain for network, block, and transaction-related info (and more), as well as sending transactions to the blockchain.

All higher-level abstractions (e.g. Wallet, Contract) that interact with the blockchain go through the Provider, so it's used for various actions like getting a wallet's balance, deploying contracts, querying their state, etc.

<<< @./snippets/provider-instantiation.ts#provider-instantiation{ts:line-numbers}

You can find more examples of Provider usage here.

Provider Options

You can provide various options on Provider instantiation to modify its behavior.

retryOptions

Calls to a fuel node via the Provider will fail if a connection cannot be established. Specifying retry options allows you to customize the way you want to handle that failure scenario before ultimately throwing an error.

NOTE: retrying is only done when a connection cannot be established. If the connection is established and the node throws an error, no retry will happen.

You can provide the following settings:

  • maxRetries - Amount of attempts to retry after initial attempt before failing the call.
  • backoff - Strategy used to define the intervals between attempts.
    • exponential (default): Doubles the delay with each attempt.
    • linear - Increases the delay linearly with each attempt.
    • fixed: Uses a constant delay between attempts.
  • baseDelay (default 150ms) - Base time in milliseconds for the backoff strategy.

<<< @./snippets/provider-options.ts#retryOptions{ts:line-numbers}

requestMiddleware

Allows you to modify the request object to add additional headers, modify the request's body, and much more.

<<< @./snippets/provider-options.ts#requestMiddleware{ts:line-numbers}

timeout

Specify the timeout in milliseconds after which every request will be aborted.

<<< @./snippets/provider-options.ts#timeout{ts:line-numbers}

fetch

Provide a custom fetch function that'll replace the default fetch call.

Note: If defined, requestMiddleware, timeout and retryOptions are applied to this custom fetch function as well.

<<< @./snippets/provider-options.ts#fetch{ts:line-numbers}

resourceCacheTTL

When using the SDK, it may be necessary to submit multiple transactions from the same account in a short period. In such cases, the SDK creates and funds these transactions, then submits them to the node.

However, if a second transaction is created before the first one is processed, there is a chance of using the same resources (UTXOs or Messages) for both transactions. This happens because the resources used in the first transaction are still unspent until the transaction is fully processed.

If the second transaction attempts to use the same resources that the first transaction has already spent, it will result in one of the following error:

Transaction is not inserted. Hash is already known

Transaction is not inserted. UTXO does not exist: {{utxoID}}

Transaction is not inserted. A higher priced tx {{txID}} is already spending this message: {{messageNonce}}

This error indicates that the resources used by the second transaction no longer exist, as the first transaction already spent them.

To prevent this issue, the SDK sets a default cache for resources to 20 seconds. This default caching mechanism ensures that resources used in a submitted transaction are not reused in subsequent transactions within the specified time. You can control the duration of this cache using the resourceCacheTTL flag. If you would like to disable caching, you can pass a value of -1 to the resourceCacheTTL parameter.

<<< @./snippets/provider-options.ts#cache-utxo{ts:line-numbers}

Note:

If you would like to submit multiple transactions without waiting for each transaction to be completed, your account must have multiple UTXOs available. If you only have one UTXO, the first transaction will spend it, and any remaining amount will be converted into a new UTXO with a different ID.

By ensuring your account has multiple UTXOs, you can effectively use the resourceCacheTTL flag to manage transactions without conflicts. For more information on UTXOs, refer to the UTXOs guide.

Pagination

Pagination is highly efficient when dealing with large sets of data. Because of this some methods from the Provider class support GraphQL cursor pagination, allowing you to efficiently navigate through data chunks.

Pagination Arguments

The pagination arguments object is used to specify the range of data you want to retrieve. It includes the following properties:

  • after: A cursor pointing to a position after which you want to retrieve items.
  • first: The number of items to retrieve after the specified cursor. This is used in conjunction with the after argument.
  • before: A cursor pointing to a position before which you want to retrieve items.
  • last: The number of items to retrieve before the specified cursor. This is used in conjunction with the before argument.

<<< @./snippets/pagination.ts#pagination-args{ts:line-numbers}

Page Info

The pageInfo object is included in the GraphQL response for requests that support cursor pagination. It provides crucial metadata about the current page of results, allowing you to understand the pagination state and determine if there are more items to fetch before or after the current set.

  • endCursor: A cursor representing the last item in the current set of results. It should be used as the after argument in subsequent queries to fetch the next set of items.
  • hasNextPage: A boolean indicating whether there are more items available after the current set.
  • startCursor: A cursor representing the first item in the current set of results. It should be used as the before argument in subsequent queries to fetch the previous set of items.
  • hasPreviousPage: A boolean indicating whether there are more items available before the current set.

<<< @./snippets/pagination.ts#pagination-page-info{ts:line-numbers}

Using Pagination

One of the methods that supports pagination is the getCoins method. This method receives three parameters:

  • address: The owner's account address
  • assetId: The asset ID of the coins (optional)
  • paginationArgs: The pagination arguments (optional)

Basic Pagination

Here is how you can use the getCoins method with pagination:

<<< @./snippets/pagination.ts#pagination-next-page{ts:line-numbers}

You can also use the paginationArgs to navigate to the previous page of results:

<<< @./snippets/pagination.ts#pagination-previous-page{ts:line-numbers}

Valid Combinations

  • Forward Pagination:

    Use after with first to retrieve items following a cursor.

<<< @./snippets/pagination.ts#pagination-forward-pagination{ts}

  • Backward Pagination:

    Use before with last to retrieve items preceding a cursor.

<<< @./snippets/pagination.ts#pagination-backward-pagination{ts}

Default Behavior

If neither assetId nor paginationArgs are provided, the getCoins method will default to the base asset ID and return the first 100 items:

<<< @./snippets/pagination.ts#pagination-default-args{ts:line-numbers}

Querying the Chain

Once you have set up a provider, you're ready to interact with the Fuel blockchain.

Let's look at a few examples below.

getBaseAssetId

The base asset is the underlying asset used to perform any transaction on a chain. This should be fetched from a provider to then be used in transactions.

<<< @./snippets/functionality/get-base-asset-id.ts#getBaseAssetId{ts:line-numbers}

getCoins

Returns UTXOs coins from an account address, optionally filtered by asset ID. This method supports pagination.

<<< @./snippets/functionality/get-coins-from-provider.ts#getCoins-1{ts:line-numbers}

This method is also implemented on the Account class and can be used without providing the address:

<<< @./snippets/functionality/get-coins-from-account.ts#getCoins-2{ts:line-numbers}

getResourcesToSpend

Returns spendable resources (coins or messages) for a transaction request. It accepts an optional third parameter, excludedIds, to exclude specific UTXO IDs or coin message nonces:

<<< @./snippets/functionality/get-resources-to-spend-from-provider.ts#getResourcesToSpend-1{ts:line-numbers}

This method is also available in the Account class and can be used without providing the address:

<<< @./snippets/functionality/get-resources-to-spend-from-account.ts#getResourcesToSpend-2{ts:line-numbers}

getBalances

Returns the sum of all UTXOs coins and unspent message coins amounts for all assets. Unlike getCoins, it only returns the total amounts, not the individual coins:

<<< @./snippets/functionality/get-balances.ts#getBalances-1{ts:line-numbers}

This method is also available in the Account class and can be used without providing the address parameter:

<<< @./snippets/functionality/get-balances.ts#getBalances-2{ts:line-numbers}

getBlocks

The getBlocks method returns blocks from the blockchain matching the given paginationArgs parameter, supporting pagination. The below code snippet shows how to get the last 10 blocks.

<<< @./snippets/functionality/get-blocks.ts#getBlocks{ts:line-numbers}

getMessageByNonce

You can use the getMessageByNonce method to retrieve a message by its nonce.

<<< @./snippets/functionality/get-messages-by-nonce.ts#getMessageByNonce{ts:line-numbers}

getMessages

You can use the getMessages method to retrieve a list of messages from the blockchain.

<<< @./snippets/functionality/get-messages.ts#getMessages{ts:line-numbers}

getMessageProof

A message proof is a cryptographic proof that a message was included in a block. You can use the getMessageProof method to retrieve a message proof for a given transaction ID and message ID.

You can retrieve a message proof by either using it's block ID:

<<< @./snippets/functionality/get-message-proof-block-id.ts#getMessageProof-blockId{ts:line-numbers}

Or by it's block height:

<<< @./snippets/functionality/get-message-proof-block-height.ts#getMessageProof-blockHeight{ts:line-numbers}

getTransactions

You can use the getTransactions method to retrieve a list of transactions from the blockchain. This is limited to 30 transactions per page.

<<< @./snippets/functionality/get-transactions.ts#getTransactions{ts:line-numbers}

Wallets

Wallets can be used for many important things, for instance:

  1. Checking your balance;
  2. Transferring coins to a destination address or contract;
  3. Signing messages and transactions;
  4. Paying for network fees when sending transactions or deploying smart contracts.

Wallets Instances

The SDK has multiple classes related to a Wallet instance:

  • Wallet: Works simply like a wrapper providing methods to create and instantiating WalletUnlocked and WalletLocked instances.

  • WalletLocked: Provides the functionalities for a locked wallet.

  • WalletUnlocked: Provides the functionalities for an unlocked wallet.

  • Account: Provides an abstraction with basic functionalities for wallets or accounts to interact with the network. It is essential to notice that both WalletLocked and WalletUnlocked extend from the Account class.

Let's explore these different approaches in the following sub-chapters.

Note: Keep in mind that you should never share your private/secret key. And in the case of wallets that were derived from a mnemonic phrase, never share your mnemonic phrase. If you're planning on storing the wallet on disk, do not store the plain private/secret key and do not store the plain mnemonic phrase. Instead, use WalletManager to encrypt its content first before saving it to disk.

Instantiating Wallets

Wallets can be instantiated in multiple ways within the SDK.

Generating new wallets

To generate a new, unlocked wallet, use the generate method. This method creates a new WalletUnlocked instance, which is immediately ready for use.

<<< @./snippets/instantiating/generate.ts#instantiating-wallets-1{ts:line-numbers}

Instantiating Unlocked Wallets

Creating WalletUnlocked instances of your existing wallets is easy and can be done in several ways:

From a private key:

<<< @./snippets/instantiating/from-private-key.ts#instantiating-wallets-2{ts:line-numbers}

From a mnemonic phrase:

<<< @./snippets/instantiating/from-mnemonic-phrase.ts#instantiating-wallets-3{ts:line-numbers}

From a seed:

<<< @./snippets/instantiating/from-seed.ts#instantiating-wallets-4{ts:line-numbers}

From a Hierarchical Deterministic (HD) derived key:

<<< @./snippets/instantiating/from-hd-derived-key.ts#instantiating-wallets-5{ts:line-numbers}

From a JSON wallet:

<<< @./snippets/instantiating/from-json-wallet.ts#instantiating-wallets-6{ts:line-numbers}

It's possible to instantiate a WalletUnlocked from a WalletLocked:

<<< @./snippets/instantiating/unlock-from-private-key.ts#instantiating-wallets-7{ts:line-numbers}

Instantiating Locked Wallets

You can also instantiate WalletLocked instances using just the wallet address:

<<< @./snippets/instantiating/from-b256-address.ts#instantiating-wallets-8{ts:line-numbers}

Connecting to a Provider

While wallets can be used independently of a Provider, operations requiring blockchain interaction will need one.

Connecting an existing wallet to a Provider:

<<< @./snippets/instantiating/connect-existing-wallet.ts#instantiating-wallets-9{ts:line-numbers}

Instantiating a wallet with a Provider:

<<< @./snippets/instantiating/connect-new-wallet.ts#instantiating-wallets-10{ts:line-numbers}

Creating a wallet from a private key

A new wallet with a randomly generated private key can be created by supplying Wallet.generate.

<<< @./snippets/access.ts#wallets{ts:line-numbers}

Alternatively, you can create a wallet from a Private Key:

<<< @./snippets/instantiating/from-wallet.ts#wallet-from-private-key{ts:line-numbers}

You can obtain an address to a private key using the Signer package

<<< @./snippets/instantiating/signer.ts#signer-address{ts:line-numbers}

Creating a wallet from mnemonic phrases

A mnemonic phrase is a cryptographically-generated sequence of words that's used to derive a private key. For instance: "oblige salon price punch saddle immune slogan rare snap desert retire surprise"; would generate the address 0xdf9d0e6c6c5f5da6e82e5e1a77974af6642bdb450a10c43f0c6910a212600185.

In addition to that, we also support Hierarchical Deterministic Wallets and derivation paths, allowing multiple wallets to be derived from a single root mnemonic. You may recognize a derivation path like:

"m/44'/60'/0'/0/0"

In simple terms, this structure enables the creation of multiple wallet addresses from the same mnemonic phrase.

The SDK gives you two wallets from mnemonic instantiation methods: one that takes a derivation path and one that uses the default derivation path, in case you don't want or don't need to configure that.

Here's how you can create wallets with both mnemonic phrases and derivation paths:

1 - Using the default derivation path m/44'/60'/0'/0/0

<<< @./snippets/mnemonic/from-mnemonic-phrases-1.ts#snippet-full{ts:line-numbers}

2 - Using a Custom Derivation Path

<<< @./snippets/mnemonic/from-mnemonic-phrases-2.ts#snippet-full{ts:line-numbers}

Encrypting and Decrypting

JSON wallets are a standardized way of storing wallets securely. They follow a specific schema and are encrypted using a password. This makes it easier to manage multiple wallets and securely store them on disk. This guide will take you through the process of encrypting and decrypting JSON wallets using the Typescript SDK.

Encrypting a Wallet

We will be calling encrypt from the WalletUnlocked instance which will take a password as the argument. It will encrypt the private key using a cipher and returns the JSON keystore wallet. You can then securely store this JSON wallet.

Here is an example of how you can accomplish this:

<<< @./snippets/encrypting-and-decrypting-wallets.ts#encrypting-and-decrypting-json-wallets-1{ts:line-numbers}

Please note that encrypt must be called within an instance of WalletUnlocked. This instance can only be achieved through passing a private key or mnemonic phrase to a locked wallet.

Decrypting a Wallet

To decrypt the JSON wallet and retrieve your private key, you can call fromEncryptedJson on a Wallet instance. It takes the encrypted JSON wallet and the password as its arguments, and returns the decrypted wallet.

Here is an example:

<<< @./snippets/encrypting-and-decrypting-json-wallets-two.ts#encrypting-and-decrypting-json-wallets-2{ts:line-numbers}

In this example, decryptedWallet is an instance of WalletUnlocked class, now available for use.

Important

Remember to securely store your encrypted JSON wallet and password. If you lose them, there will be no way to recover your wallet. For security reasons, avoid sharing your private key, encrypted JSON wallet or password with anyone.

Checking balances

To check the balance of a specific asset, you can use getBalance method. This function aggregates the amounts of all unspent coins of the given asset in your wallet.

<<< @./snippets/checking-balances.ts#checking-balances-1{ts:line-numbers}

To retrieve the balances of all assets in your wallet, use the getBalances method, it returns an array of CoinQuantity. This is useful for getting a comprehensive view of your holdings.

<<< @./snippets/checking-balances-two.ts#checking-balances-2{ts:line-numbers}

Wallet Transferring

This guide provides instructions for transferring assets between wallets and contracts using the SDK. It includes methods to validate balances and initiate and configure transfer requests.

Transferring Assets Between Accounts

The transfer method initiates a transaction request that transfers an asset from one wallet to another. This method requires three parameters:

  1. The receiver's wallet address
  2. The amount of the asset to be transferred
  3. The ID of the asset to be transferred (optional - defaults to the base asset ID)

Upon execution, this function returns a promise that resolves to a transaction response. To wait for the transaction to be processed, call response.waitForResult().

Example

Here is an example of how to use the transfer function:

<<< @./snippets/wallet-transferring/between-accounts.ts#transferring-assets-1{ts:line-numbers}

In the previous example, we used the transfer method which creates a ScriptTransactionRequest, populates its data with the provided transfer information and submits the transaction.

However, there may be times when you need the Transaction ID before actually submitting it to the node. To achieve this, you can simply call the createTransfer method instead.

This method also creates a ScriptTransactionRequest and populates it with the provided data but returns the request object prior to submission.

<<< @./snippets/wallet-transferring/create-transfer.ts#transferring-assets-2{ts:line-numbers}

Note: Any changes made to a transaction request will alter the transaction ID. Therefore, you should only get the transaction ID after all modifications have been made.

<<< @./snippets/wallet-transferring/create-transfer-2.ts#transferring-assets-3{ts:line-numbers}

Transferring Assets To Multiple Wallets

To transfer assets to multiple wallets, use the Account.batchTransfer method:

<<< @./snippets/transfers/batch-transfer.ts#wallet-transferring-6{ts:line-numbers}

Transferring Assets To Contracts

When transferring assets to a deployed contract, we use the transferToContract method, which shares a similar parameter structure with the transfer method.

However, instead of supplying the target wallet's address, as done in destination.address for the transfer method, we need to provide an instance of Address created from the deployed contract id.

If you have the Contract instance of the deployed contract, you can simply use its id property. However, if the contract was deployed with forc deploy or not by you, you will likely only have its ID in a hex string format. In such cases, you can create an Address instance from the contract ID using new Address('0x123...').

Here's an example demonstrating how to use transferToContract:

<<< @./snippets/wallet-transferring/transferring-to-contracts.ts#transferring-assets-4{ts:line-numbers}

Note: Use transferToContract exclusively for transfers to a contract. For transfers to an account address, use transfer instead.

Transferring Assets To Multiple Contracts

Similar to the Account.batchTransfer method, you can transfer multiple assets to multiple contracts using the Account.batchTransferToContracts method. Here's how it works:

<<< @./snippets/wallet-transferring/transferring-to-multiple-contracts.ts#transferring-assets-5{ts:line-numbers}

Always remember to call the waitForResult() function on the transaction response. That ensures the transaction has been mined successfully before proceeding.

Note: Use batchTransferToContracts solely for transferring assets to contracts. Do not use account addresses with this method. For multiple account transfers, use batchTransfer instead.

Checking Balances

Before you transfer assets, please make sure your wallet has enough funds. Attempting a transfer without enough funds will result in the error: The transaction does not have enough funds to cover its execution.

You can see how to check your balance at the checking-balances page.

Signing

Signing Messages

Signing messages with a wallet is a fundamental security practice in a blockchain environment. It can be used to verify ownership and ensure the integrity of data.

Here's how to use the wallet.signMessage method to sign messages (as string):

<<< @./snippets/signing/sign-message.ts#signing-1{ts:line-numbers}

The signMessage method internally:

  • Hashes the message (via hashMessage)
  • Signs the hashed message using the wallet's private key
  • Returns the signature as a hex string

The hashMessage helper will:

  • Performs a SHA-256 hash on the UTF-8 encoded message.

The recoverAddress method from the Signer class will take the hashed message and the signature to recover the signer's address. This confirms that the signature was created by the holder of the private key associated with that address, ensuring the authenticity and integrity of the signed message.

Signing Personal Message

We can also sign arbitrary data, not just strings. This is possible by passing an object containing the personalSign property to the hashMessage and signMessage methods:

<<< @./snippets/signing/sign-personal-message.ts#signing-personal-message{ts:line-numbers}

The primary difference between this personal message signing and message signing is the underlying hashing format.

To format the message, we use a similar approach to a EIP-191:

\x19Fuel Signed Message:\n<message length><message>

Note: We still hash using SHA-256, unlike Ethereum's EIP-191 which uses Keccak-256.

Signing Transactions

Signing a transaction involves using your wallet to sign the transaction ID (also known as transaction hash) to authorize the use of your resources. Here's how it works:

  1. Generate a Signature: Using the wallet to create a signature based on the transaction ID.

  2. Using the Signature on the transaction: Place the signature in the transaction's witnesses array. Each Coin / Message input should have a matching witnessIndex. This index indicates your signature's location within the witnesses array.

  3. Security Mechanism: The transaction ID is derived from the transaction bytes (excluding the witnesses). If the transaction changes, the ID changes, making any previous signatures invalid. This ensures no unauthorized changes can be made after signing.

The following code snippet exemplifies how a Transaction can be signed:

<<< @./snippets/signing/sign-transaction.ts#signing-2{ts:line-numbers}

Similar to the sign message example, the previous code used Signer.recoverAddress to get the wallet's address from the transaction ID and the signed data.

When using your wallet to submit a transaction with wallet.sendTransaction(), the SDK already handles these steps related to signing the transaction and adding the signature to the witnesses array. Because of that, you can skip this in most cases:

<<< @./snippets/signing/fund-transaction.ts#signing-3{ts:line-numbers}

Connectors

Fuel Wallet Connectors offer a standardized interface to integrate multiple wallets with your DApps, simplifying wallet integration and ensuring smooth user interactions.

Fuel Connectors

Fuel Connectors are a set of standardized interfaces that provide a way to interact with various wallets and services. They offer a consistent way to interact with different wallets and services, allowing developers to focus on building their applications rather than worrying about wallet integration.

To build your own wallet integration, you can create a custom connector that extends the abstract FuelConnector class. This interface provides a set of methods and events that allow you to interact with the wallet and handle various operations such as connecting, disconnecting, signing messages, and sending transactions.

<<< @./snippets/connectors.ts#fuel-connector-extends{ts:line-numbers}

Properties

The FuelConnector abstract class provides several properties that should be implemented to provide information about the connector.

name

The name property is simply a string on the connector that serves as an identifier and will be displayed to the end-user when selecting a connector.

<<< @./snippets/connectors.ts#fuel-connector-name{ts:line-numbers}

external

The external property is simply a boolean that indicates when a connector is external or not. Connectors are considered external, or non-native, when they do not support the Fuel Network (e.g. Solana, WalletConnect).

metadata

The metadata property on the connector provides additional information about the connector. This information will be displayed to the end-user when selecting a connector. The following is the structure of the metadata object:

<<< @/../../../packages/account/src/connectors/types/connector-metadata.ts#fuel-connector-metadata{ts:line-numbers}

install

The metadata.install property (required) is used to provide information about how to install the connector.

The install object requires three properties:

  • action (required) - a string that will contain an action string that will be displayed to the user (e.g. "Install").

  • link (required) - a string that will contain a URL that will be opened when the user clicks the action.

  • description (required) - a string that will contain a description of the installation process.

<<< @./snippets/connectors.ts#fuel-connector-metadata-install{ts:line-numbers}

image

The metadata.image property (optional) provides an image that will be displayed to the end-user when selecting a connector. The image will be a URL to the image to be displayed (this can be an inline data URI, encoded in base64).

<<< @./snippets/connectors.ts#fuel-connector-metadata-image{ts:line-numbers}

You can even define a light and dark theme for the image by providing an object with the light and dark keys (these will take a similar URI as above).

<<< @./snippets/connectors.ts#fuel-connector-metadata-image-theme{ts:line-numbers}

Events

The FuelConnector class provides a number of events that enable developers to listen for changes in the connector state. As part of implementing a custom connector, you can emit these events to notify the consumer dApp of changes.

accounts

The accounts event is emitted every time a connector's accounts change. The event data is an array of string addresses available on the network.

<<< @./snippets/connectors.ts#fuel-connector-events-accounts{ts:line-numbers}

connectors

The connectors event is emitted when the connectors are initialized. The event data is an array of FuelConnector objects available on the network.

<<< @./snippets/connectors.ts#fuel-connector-events-connectors{ts:line-numbers}

currentConnector

The currentConnector event is emitted every time the current connector changes. The event data is a FuelConnector object that is currently connected.

<<< @./snippets/connectors.ts#fuel-connector-events-currentConnector{ts:line-numbers}

currentAccount

The currentAccount event is emitted every time the current account changes. The event data is a string containing the current account address.

<<< @./snippets/connectors.ts#fuel-connector-events-currentAccount{ts:line-numbers}

connection

The connection event is emitted every time the connection status changes. The event data is a boolean value that is true if the connection is established and false otherwise.

<<< @./snippets/connectors.ts#fuel-connector-events-connection{ts:line-numbers}

networks

The networks event is emitted every time the network changes. The event data will be a Network object containing the current network information.

<<< @./snippets/connectors.ts#fuel-connector-events-networks{ts:line-numbers}

currentNetwork

The currentNetwork event is emitted every time the current network changes. The event data will be a Network object containing the current network information.

<<< @./snippets/connectors.ts#fuel-connector-events-currentNetwork{ts:line-numbers}

assets

The assets event is emitted every time the assets change. The event data will be an array of Asset objects available on the network.

<<< @./snippets/connectors.ts#fuel-connector-events-assets{ts:line-numbers}

abis

The abis event is emitted every time an ABI is added to a connector. The event data will be an array of FuelABI object.

<<< @./snippets/connectors.ts#fuel-connector-events-assets{ts:line-numbers}

Methods

The FuelConnector abstract class provides a number of methods that can be implemented to perform various functions. Not all the methods are required to be implemented; if you choose not to implement a given method, then just don't include it in your connector.

ping

The ping method is used to check if the connector is available and connected.

It will return a promise that resolves to true if the connector is available and connected; otherwise, it will resolve to false.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-ping{ts:line-numbers}

version

The version method is used to get the current supported version of the connector. It returns a promise that resolves to an object containing the app and network versions.

The returned version strings can be in a range of formats:

  • Caret Ranges (e.g. ^1.2.3)
  • Tilde Ranges (e.g. ~1.2.3)
  • Exact Versions (e.g. 1.2.3)

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-version{ts:line-numbers}

isConnected

The isConnected method informs if the connector is currently connected.

It will return a promise that resolves to true if the connector is established and currently connected; otherwise, it will return false.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-isConnected{ts:line-numbers}

connect

The connect method initiates the current connectors authorization flow if a connection has not already been made.

It will return a promise that resolves to true if the connection has been established successfully, or false if the user has rejected it.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-connect{ts:line-numbers}

disconnect

The disconnect method revokes the authorization of the current connector (provided by the connect methods).

It will return a promise that resolves to true if the disconnection is successful; otherwise, it will resolve to false.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-connect{ts:line-numbers}

accounts

The accounts method should return a list of all the accounts for the current connection.

It returns a promise that resolves to an array of addresses, pointing to the accounts currently available on the network.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-accounts{ts:line-numbers}

currentAccount

The currentAccount method will return the default account address if it's authorized with the connection.

It will return a promise to resolve the issue to an address, or if the account is not authorized for the connection, it will return null.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-currentAccount{ts:line-numbers}

signMessage

The signMessage method initiates the sign message flow for the current connection.

It requires two arguments:

  • address (string)
  • message (string)

Providing the message signing flow is successful, it will return the message signature (as a string).

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-signMessage{ts:line-numbers}

sendTransaction

The signTransaction method initiates the send transaction flow for the current connection.

It requires two arguments:

It will return the transaction signature (as a string) if it is successfully signed.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-sendTransaction{ts:line-numbers}

assets

The assets method returns a list of all the assets available for the current connection.

It will return a promise that will resolve to an array of assets (see Asset) that are available on the network.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-assets{ts:line-numbers}

addAsset

The addAsset method adds asset metadata to the connector.

It requires a single argument:

It returns a promise that resolves to true if the asset is successfully added; otherwise, it resolves to false.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-addAsset{ts:line-numbers}

addAssets

The addAssets method adds multiple asset metadata to the connector.

It requires a single argument:

  • assets (an Array of Asset).

Returns a promise that resolves to true if the assets are successfully added; otherwise, resolves to false.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-addAssets{ts:line-numbers}

addNetwork

The addNetwork method starts the add network flow for the current connection.

It requires a single argument:

  • networkUrl (string)

Returns a promise that resolves to true if the network is successfully added; otherwise, false.

It should throw an error if the network is not available or the network already exists.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-addNetwork{ts:line-numbers}

networks

The networks method returns a list of all the networks available for the current connection.

Returns a promise that resolves to an array of available networks (see Network).

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-networks{ts:line-numbers}

currentNetwork

The currentNetwork method will return the current network that is connected.

It will return a promise that will resolve to the current network (see Network).

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-currentNetwork{ts:line-numbers}

selectNetwork

The selectNetwork method requests the user to select a network for the current connection.

It requires a single argument:

You call this method with either the providerUrl or chainId to select the network.

It will return a promise that resolves to true if the network is successfully selected; otherwise, it will return false.

It should throw an error if the network is not available or the network does not exist.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-selectNetwork{ts:line-numbers}

addABI

The addABI method adds ABI information about a contract to the connector. This operation does not require an authorized connection.

It requires two arguments:

  • contractId (string)
  • abi (FuelABI).

It will return a promise that will resolve to true if the ABI is successfully added; otherwise false.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-addABI{ts:line-numbers}

getABI

The getABI method is used to get the ABI information that is sorted about a contract.

It requires a single argument:

  • contractId (string)

Returns a promise that resolves to the ABI information (as a FuelABI) or null if the data is unavailable.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-getABI{ts:line-numbers}

hasABI

The hasABI method checks if the ABI information is available for a contract.

It requires a single argument:

  • contractId (string)

Returns a promise that resolves to true if the ABI information is available; otherwise false.

<<< @/../../../packages/account/src/connectors/fuel-connector.ts#fuel-connector-method-hasABI{ts:line-numbers}

Connectors Manager

The TS SDK exports the Fuel class, which serves as the connectors manager. This class provides the interface for interacting with the TS SDK and the broader Fuel ecosystem.

It can be instantiated as follows:

<<< @./snippets/fuel-instantiation-one.ts#fuel-instantiation-1{ts:line-numbers}

[!NOTE] Note We recommend initializing the Fuel class with the init method to avoid any potential race conditions that may arise from the async nature of instantiating a connector.

Options

Several options can be passed to the Fuel connector manager:

connectors

The connectors option provides a list of connectors with which the Fuel connector manager can interact. The manager interacts with the connectors, which in turn handle communication with the respective wallet. You can find a list of all the connectors in our FuelLabs/fuel-connectors.

Below, we initialize the manager using the defaultConnectors method which provides an array of all the default connectors available in the fuel-connectors package. It's being mocked here for the purposes of this example, but you can provide your own custom connectors. Supplying the devMode flag as true will enable the development wallet for the connectors (to install visit our wallet documentation).

<<< @./snippets/fuel-instantiation-options.ts#fuel-options-connectors{ts:line-numbers}

storage

The storage is used internally to store the current connector state. It can be overridden by passing an instance that extends the StorageAbstract class.

<<< @./snippets/fuel-options-storage-memory.ts#fuel-options-storage-memory{ts:line-numbers}

The default behavior will use LocalStorage if the window is available:

<<< @./snippets/fuel-options-storage-local.ts#fuel-options-storage-local{ts:line-numbers}

targetObject

The targetObject provides a target with which the Fuel manager can interact. Used for registering events and can be overridden as follows:

<<< @./snippets/fuel-options-target-object.ts#fuel-options-target-object{ts:line-numbers}

Methods

The Fuel manager provides several methods to interact with the Manager:

All methods from connectors

The Fuel manager provides all the methods available from the connected connectors. Thus, you can interact with the current connector as if you were interacting with the Fuel manager directly.

If no current connector is available or connected, it will throw an error.

connectors

The connectors method gets the current list of installed and connected connectors.

<<< @/../../../packages/account/src/connectors/fuel.ts#connector-manager-method-connectors{ts:line-numbers}

getConnector

The getConnector method resolves a connector by its name. This is useful for finding a specific connector with which to interact. If the connector is not found, it will return null.

<<< @/../../../packages/account/src/connectors/fuel.ts#connector-manager-method-getConnector{ts:line-numbers}

hasConnector

The hasConnector method will return true under the following conditions:

  • There is a current connector that is connected.
  • A connector is connected within two seconds of calling the method.

<<< @/../../../packages/account/src/connectors/fuel.ts#connector-manager-method-hasConnector{ts:line-numbers}

selectConnector

The selectConnector method accepts a connector name and will return true when it is available and connected. Otherwise, if not found or unavailable, it will return false.

<<< @/../../../packages/account/src/connectors/fuel.ts#connector-manager-method-selectConnector{ts:line-numbers}

currentConnector

The currentConnector method will return the current connector that is connected or if one is available and connected, otherwise it'll return null or undefined.

<<< @/../../../packages/account/src/connectors/fuel.ts#connector-manager-method-currentConnector{ts:line-numbers}

getWallet

The getWallet method accepts an address (string or instance) as the first parameter and a provider or network as the second parameter. It will return an Account instance for the given address (providing it is valid).

The provider or network will default to the current network if not provided. When a provider cannot be resolved, it will throw an INVALID_PROVIDER error.

<<< @/../../../packages/account/src/connectors/fuel.ts#connector-manager-method-getWallet{ts:line-numbers}

clean

The clean method removes all the data currently stored in the storage instance.

<<< @/../../../packages/account/src/connectors/fuel.ts#connector-manager-method-clean{ts:line-numbers}

unsubscribe

The unsubscribe method removes all currently registered event listeners.

<<< @/../../../packages/account/src/connectors/fuel.ts#connector-manager-method-unsubscribe{ts:line-numbers}

destroy

The destroy method unsubscribes from all the event listeners and clears the storage.

<<< @/../../../packages/account/src/connectors/fuel.ts#connector-manager-method-destroy{ts:line-numbers}

Learning Resources

For a deeper understanding of Fuel Connectors and how to start using them in your projects, consider the following resources:

Wallet Manager

The WalletManager is a robust tool designed for managing vaults of wallets. It offers robust management of vaults, including support for custom storage and powerful encryption of all held vaults.

Key Features

Managing Vaults with WalletManager

This includes adding new wallets to specific vaults, retrieving all wallets from a vault, exporting specific vaults, and exporting private keys. The WalletManager class currently supports two types of vaults: PrivateKeyVault and MnemonicVault.

Custom Storage Solutions

The WalletManager supports defining a custom storage solution, allowing you to specify how and where the encrypted vaults are saved. With support for custom storage, you can make the WalletManager to fit your specific needs and security requirements.

Locking and Unlocking WalletManager

The WalletManager implements an automatic encryption mechanism, securely saving the wallet's held vaults. This not only preserves the state of your vaults but also ensures robust protection of the stored information. When needed, you can easily unlock and decrypt the vaults using the previously defined password.

Getting Started with WalletManager

This guide provides step-by-step instructions on how to use WalletManager.

Instantiating WalletManager

The WalletManager constructor accepts an optional object to define its storage. The storage describes how and where the WalletManager will store its vaults of wallets. If storage is not provided, the WalletManager uses a default one that does not persist data.

For now, let's keep it simple and not worry about the storage. Later we will discuss it in more detail.

To instantiate a WalletManager you can simply:

<<< @./snippets/getting-started-with-wallet-manager.ts#getting-started-with-wallet-manager-1{ts:line-numbers}

Setting WalletManager Password

By default, a WalletManager instance is locked when created. Before using it, you need to unlock it by setting a password. You can do this by calling the unlock method.

<<< @./snippets/getting-started-with-wallet-manager.ts#getting-started-with-wallet-manager-2{ts:line-numbers}

Once your WalletManager is unlocked, it can manage your wallets.

Managing Vaults with WalletManager

A vault in WalletManager serves as a secure container for wallets. The WalletManager manages wallets by interacting with these vaults, supporting operations such as getAccounts, which returns public information about all wallets stored in the vault, and exportAccount, which exports a private key for a given wallet address.

To add a vault, we utilize the addVault method. Here's how we can create a private key vault and add a private key from a wallet we own:

<<< @./snippets/getting-started-with-wallet-manager.ts#getting-started-with-wallet-manager-3{ts:line-numbers}

The addVault method requires an object with three properties: type, secret, and title. The WalletManager currently supports two types of vaults: privateKeyVault and mnemonicVault. For the secret, we use our wallet's private key, and for the title, we can provide a custom name.

By running this code, WalletManager creates a new vault instance of the type privateKey and adds one account (our wallet) to this newly created vault.

A key feature of the WalletManager is its ability to manage multiple vaults, even of the same type. This implies that if you run the addVault method again, with the same parameters, WalletManager will create another vault of the type privateKey, holding the same wallet. Here's an example:

<<< @./snippets/getting-started-with-wallet-manager.ts#getting-started-with-wallet-manager-4{ts:line-numbers}

After executing this, you will find that your WalletManager is managing two privateKey vaults, both storing the same wallet.

Remember, both title and secret are optional when adding vaults, but providing a title makes it easier to manage your vaults and wallets. If you add a vault without providing a secret, this will result in one new account (wallet) being generated by the vault it self.

Using The WalletManager

With your WalletManager set up, you can now access your vaults and wallets. Here's how to retrieve the details of your vaults:

<<< @./snippets/getting-started-with-wallet-manager.ts#getting-started-with-wallet-manager-5{ts:line-numbers}

This will output something like this:

<<< @./snippets/getting-started-with-wallet-manager.ts#getting-started-with-wallet-manager-6{bash:line-numbers}

As you can see, the WalletManager assigns unique vaultIds for each vault. The first vault you added has a vaultId of 0, and the second one has a vaultId of 1.

Let's retrieve your wallet instance with the getWallet method:

<<< @./snippets/getting-started-with-wallet-manager.ts#getting-started-with-wallet-manager-7{ts:line-numbers}

This guide walked through the steps to instantiate a WalletManager, set up its first vault, and retrieve vault information. The following sections will explore more functionalities of WalletManager, and go deeper into the usage of its vaults and the details of its storage system.

Locking and Unlocking WalletManager

This guide will walk you through the process of managing the lock state of your wallets using the WalletManager.

Initializing and Unlocking the WalletManager

As mentioned earlier, a WalletManager instance begins in a locked state. Before usage, you need to unlock it by providing a password via the unlock method.

<<< @./snippets/locking-and-unlocking-wallet-manager.ts#locking-and-unlocking-wallet-manager-1{ts:line-numbers}

Locking the WalletManager

When you lock the WalletManager using the lock method, all its vaults and associated accounts (wallets) are cleared. This clearance is possible due to the encryption and saving of all data by the storage system. WalletManager frequently uses the storage system to preserve its state. Consequently, sensitive operations including exporting vaults, private keys, accessing wallets, and saving/loading the WalletManager state are not possible when it is locked.

<<< @./snippets/locking-and-unlocking-wallet-manager.ts#locking-and-unlocking-wallet-manager-2{ts:line-numbers}

Remember, it's crucial to lock your WalletManager when it's not in use to ensure the safety of your funds.

Reaccessing Your Wallets by Unlocking the WalletManager

The unlock method requires the previously set password to unlock the WalletManager and all its vaults. The password decrypts the stored vaults, allowing WalletManager to load its saved data.

<<< @./snippets/locking-and-unlocking-wallet-manager.ts#locking-and-unlocking-wallet-manager-3{ts:line-numbers}

Providing an incorrect password will result in an error. However, when unlocked successfully, WalletManager is ready for use again.

Verifying the Lock State

You can confirm the current lock state of the WalletManager by using the isLocked method:

<<< @./snippets/locking-and-unlocking-wallet-manager.ts#locking-and-unlocking-wallet-manager-4{ts:line-numbers}

Updating the Password

To change the current password, invoke the updatePassphrase method, and provide both the old and new passwords:

<<< @./snippets/locking-and-unlocking-wallet-manager.ts#locking-and-unlocking-wallet-manager-5{ts:line-numbers}

Reminder: Always Lock Your WalletManager

Always ensure you lock the WalletManager after completing operations. This step is critical for securing your wallets.

<<< @./snippets/locking-and-unlocking-wallet-manager.ts#locking-and-unlocking-wallet-manager-6{ts:line-numbers}

By using WalletManager to manage lock and unlock states, you introduce an additional layer of security. Never forget to lock your WalletManager when it's not in use.

Locking and Unlocking

The kinds of operations we can perform with a Wallet instance depend on whether or not we have access to the wallet's private key.

In order to differentiate between Wallet instances that know their private key and those that do not, we use the WalletUnlocked and WalletLocked types respectively.

Wallet States

The WalletUnlocked type represents a wallet whose private key is known and stored internally in memory. A wallet must be of type WalletUnlocked in order to perform operations that involve signing messages or transactions.

The WalletLocked type represents a wallet whose private key is not known or stored in memory. Instead, WalletLocked only knows its public address. A WalletLocked cannot be used to sign transactions, however it may still perform a whole suite of useful operations including listing transactions, assets, querying balances, and so on.

Note that the WalletUnlocked type implements most methods available on the WalletLocked type. In other words, WalletUnlocked can be thought of as a thin wrapper around WalletLocked that provides greater access via its private key.

Basic Example

<<< @./snippets/access.ts#wallets{ts:line-numbers}

Optional Provider

You can choose not to pass through a provider argument on Wallet construction:

<<< @./snippets/wallet-optional-provider.ts#wallet-optional-provider{ts:line-numbers}

Transitioning States

A WalletLocked instance can be unlocked by providing the private key:

<<< @./snippets/locked-to-unlocked.ts#wallet-locked-to-unlocked{ts:line-numbers}

A WalletUnlocked instance can be locked using the lock method:

<<< @./snippets/unlocked-to-locked.ts#wallet-unlocked-to-locked{ts:line-numbers}

Most wallet constructors that create or generate a new wallet are provided on the WalletUnlocked type. Consider locking the wallet with the lock method after the new private key has been handled in order to reduce the scope in which the wallet's private key is stored in memory.

Design Guidelines

When designing APIs that accept a wallet as an input, we should think carefully about the kind of access that we require. API developers should aim to minimise their usage of WalletUnlocked in order to ensure private keys are stored in memory no longer than necessary to reduce the surface area for attacks and vulnerabilities in downstream libraries and applications.

Full Example

For a full example of how to lock and unlock a wallet, see the snippet below:

<<< @./snippets/access.ts#full{ts:line-numbers}

Contracts

In the Fuel Network, contracts play a crucial role in facilitating interactions between users and the decentralized applications built on top of the network. Once you've deployed a contract, you may want to perform various tasks such as:

  1. Calling contract methods;
  2. Configuring call and transaction parameters like gas price, byte price, and gas limit;
  3. Forwarding coins and gas in your contract calls;
  4. Reading and interpreting returned values and logs.

For instance, consider a Sway contract with two ABI methods called echo_str_8(str[8]) and echo_u8(u8). After deploying the contract, you can call the methods as follows:

<<< @./snippets/introduction.ts#method-calls{ts:line-numbers}

The example above demonstrates a simple contract call using default configurations. The following sections will explore how to further configure various parameters for contract calls, allowing for more advanced interactions with your deployed contracts in the Fuel Network.

Interacting With Contracts

There are 4 ways to interact with contracts: get, dryRun, simulate, call.

get

The get method should be used to read data from the blockchain without using resources. It can be used with an unfunded wallet or even without a wallet at all:

<<< @./snippets/methods/get.ts#interacting-with-contracts-1{ts:line-numbers}

dryRun

The dryRun method should be used to dry-run a contract call. It does not spend resources and can be used with an unfunded wallet or even without a wallet at all:

<<< @./snippets/methods/dry-run.ts#interacting-with-contracts-2{ts:line-numbers}

simulate

The simulate method should be used to dry-run a contract call, ensuring that the wallet used has sufficient funds to cover the transaction fees, without consuming any resources.

A funded wallet it's required:

<<< @./snippets/methods/simulate.ts#interacting-with-contracts-3{ts:line-numbers}

call

The call method submits a real contract call transaction to the node, resolving immediately upon submission and returning a transactionId along with a waitForResult callback to wait for transaction execution. This behavior aligns with the natural behaviour of blockchains, where transactions may take a few seconds before being recorded on the chain.

Real resources are consumed, and any operations executed by the contract function will be processed on the blockchain.

<<< @./snippets/methods/call.ts#interacting-with-contracts-4{ts:line-numbers}

isReadOnly (utility)

If you want to figure out whether a function is read-only, you can use the isReadOnly method:

<<< @./snippets/methods/is-read-only.ts#is-function-readonly-1{ts:line-numbers}

If the function is read-only, you can use the get method to retrieve onchain data without spending gas.

If the function is not read-only you will have to use the call method to submit a transaction onchain which incurs a gas fee.

Call Parameters

When interacting with contracts, you can configure specific parameters for contract calls using the callParams method. The available call parameters are:

  1. forward
  2. gasLimit

Note: Setting transaction parameters is also available when calling contracts. More information on this can be found at Transaction Parameters.

The contract in use in this section has the following implementation:

<<< @/../../docs/sway/return-context/src/main.sw#return-context-contract{rust:line-numbers}

Forward Parameter

The forward parameter allows the sending of a specific amount of coins to a contract when a function is called. This is useful when a contract function requires coins for its execution, such as paying fees or transferring funds. The forward parameter helps you control the resources allocated to the contract call and offers protection against potentially costly operations.

<<< @./snippets/call-parameters/forward.ts#forward{ts:line-numbers}

Gas Limit Parameter

The gasLimit refers to the maximum amount of gas that can be consumed specifically by the contract call itself, separate from the rest of the transaction.

<<< @./snippets/call-parameters/gas-fee.ts#gas-fee{ts:line-numbers}

Call Parameter gasLimit vs Transaction Parameter gasLimit

The call parameter gasLimit sets the maximum gas allowed for the actual contract call, whereas the transaction parameter gasLimit (see Transaction Parameters) sets the maximum gas allowed for the entire transaction and constrains the gasLimit call parameter. If the call parameter gasLimit is set to a value greater than the available transaction gas, then the entire available transaction gas will be allocated for the contract call execution.

If you don't set the gasLimit for the call, the transaction gasLimit will be applied.

Setting Both Parameters

You can set both call parameters and transaction parameters within the same contract function call.

<<< @./snippets/call-parameters/setting-both-parameters.ts#setting-both-parameters{ts:line-numbers}

Contract Balance

When working with contracts, it's crucial to be aware of the available contract balance of an asset while paying for costly operations. This guide will explain the getBalance method in the Contract class, which allows you to check a contract's available balance.

The getBalance Method

The Contract.getBalance method retrieves the available balance of a specific asset on your contract. This method is particularly useful for determining the remaining balance after sending assets to a contract and executing contract calls.

It is important to note that this method returns the total available contract balance, regardless of how often assets have been sent to it or spent.

Checking Contract Balance

Consider a simple contract that transfers a specified amount of a given asset to an address:

<<< @/../../docs/sway/transfer-to-address/src/main.sw#full{rust:line-numbers}

The transfer function has three parameters:

  1. amount_to_transfer: The amount that is being transferred.

  2. asset: The address of the deployed contract token.

  3. recipient: The address of the receiver's wallet.

The transfer function calls the built-in Sway function transfer_to_address, which does precisely what the name suggests.

Let's execute this contract and use the getBalance method to validate the remaining asset amount the contract has left to spend.

<<< @./snippets/contract-balance.ts#example{ts:line-numbers}

In this example, we first forward an asset amount greater than the amount required for the transfer, and then we execute the contract call.

Finally, we use the getBalance method to confirm that the contract balance is precisely the total forwarded amount minus the transferred amount.

Estimating Contract Call Cost

The FunctionInvocationScope.getTransactionCost method allows you to estimate the cost of a specific contract call. The return type, TransactionCost, is an object containing relevant information for the estimation:

<<< @/../../../packages/account/src/providers/provider.ts#cost-estimation-1{ts:line-numbers}

The following example demonstrates how to get the estimated transaction cost for:

1. Single contract call transaction:

<<< @./snippets/cost-estimation.ts#cost-estimation-1{ts:line-numbers}

2. Multiple contract calls transaction:

<<< @./snippets/cost-estimation.ts#cost-estimation-2{ts:line-numbers}

You can use the transaction cost estimation to set the gas limit for an actual call or display the estimated cost to the user.

Transaction Dependency Estimation

In variable outputs, we mention that a contract call might require you to manually specify external contracts or variable outputs.

However, by default the SDK always automatically estimates these dependencies and double-checks if everything is in order whenever you invoke a contract function or attempt to send a transaction.

The SDK uses the Provider.estimateTxDependencies method to set any missing dependencies identified during the estimation process. This requires simulating the transaction a few times in the background.

While relying on the SDK's automatic estimation is a decent default behavior, we recommend manually specifying the dependencies if they are known in advance to avoid the performance impact of the estimation process.

Variable Outputs

Sway includes robust functions for transferring assets to wallets and contracts.

When using these transfer functions within your Sway projects, it is important to be aware that each call will require an Output Variable within the Outputs of the transaction.

For instance, if a contract function calls a Sway transfer function 3 times, it will require 3 Output Variables present within the list of outputs in your transaction.

Example: Sway functions that requires Output Variable

<<< @/../../docs/sway/token/src/main.sw#variable-outputs-1{ts:line-numbers}

Adding Variable Outputs to the contract call

When your contract invokes any of these functions, or if it calls a function that leads to another contract invoking these functions, you need to add the appropriate number of Output Variables.

This can be done as shown in the following example:

<<< @./snippets/utilities/variable-outputs.ts#variable-outputs-2{ts:line-numbers}

In the TypeScript SDK, the Output Variables are automatically added to the transaction's list of outputs.

This process is done by a brute-force strategy, performing sequential dry runs until no errors are returned. This method identifies the number of Output Variables required to process the transaction.

However, this can significantly delay the transaction processing. Therefore, it is highly recommended to manually add the correct number of Output Variables before submitting the transaction.

Working with Contract Logs

When you log a value within a contract method, it generates a log entry that is added to the log receipt, and the variable type is recorded in the contract's ABI. The SDK enables you to parse these values into TypeScript types.

Consider the following example contract:

<<< @/../../docs/sway/log-values/src/main.sw#log-1{rust:line-numbers}

To access the logged values in TypeScript, use the logs property in the response of a contract call. The logs data will be stored in an Array<any>:

<<< @./snippets/logs.ts#full{ts:line-numbers}

This approach allows you to work seamlessly with logged values in your contract, making it easier to understand and debug the contract's behavior.

Inter-Contract Calls with the SDK

This guide explains how to use the SDK to execute a contract call where one contract interacts with another contract. We will use a simple scenario involving a SimpleToken contract and a TokenDepositor contract.

SimpleToken and TokenDepositor Contracts

In this example, we have a SimpleToken contract representing a basic token contract capable of holding balances for different addresses. We also have a TokenDepositor contract that deposits tokens into the SimpleToken contract.

Contract: SimpleToken

Here's a simple token contract that allows holding balances:

<<< @/../../docs/sway/simple-token/src/main.sw#inter-contract-calls-1{rs:line-numbers}

Contract: TokenDepositor

The TokenDepositor contract imports the SimpleToken contract and calls its deposit function to deposit tokens:

<<< @/../../docs/sway/token-depositor/src/main.sw#inter-contract-calls-2{rs:line-numbers}

Inter-contract calls using the SDK

Once both contracts are deployed, we can use the SDK to make the TokenDepositor contract to call the SimpleToken contract.

<<< @./snippets/inter-contract-calls.ts#full{ts:line-numbers}

Pay attention to the method addContracts called by the TokenDepositor contract. This method accepts an array of instances of deployed contracts. Without calling this method, the inter-contract call will not work.

Multiple Contract Calls

You can execute multiple contract calls in a single transaction, either to the same contract or to different contracts. This can improve efficiency and reduce the overall transaction costs.

Same Contract Multi Calls

Use the multiCall method to call multiple functions on the same contract in a single transaction:

<<< @./snippets/multi-call/same-contract.ts#multicall-1{ts:line-numbers}

Different Contracts Multi Calls

The multiCall method also allows you to execute multiple contract calls to distinct contracts within a single transaction:

<<< @./snippets/multi-call/different-contracts.ts#multicall-2{ts:line-numbers}

You can also chain supported contract call methods, like callParams, for each contract call:

<<< @./snippets/multi-call/different-contracts-chain-methods.ts#multicall-3{ts:line-numbers}

When using multiCall, the contract calls are queued and executed only after invoking one of the following methods: .get, .simulate, or .call.

Using multiCall for Read-Only Contract Calls

When you need to read data from multiple contracts, the multiCall method can perform multiple read-only calls in a single transaction. This minimizes the number of requests sent to the network and consolidates data retrieval, making your dApp interactions more efficient.

<<< @./snippets/multi-call/different-contracts-readonly.ts#multicall-4{ts:line-numbers}

Making Calls with Different Wallets or Providers

This guide demonstrates how to make contract calls using different wallets and providers by passing either an Account or a Provider to the contract on instantiation.

Changing Wallets

To change the wallet associated with a contract instance, assign a new wallet to the instance's account property. This allows you to make contract calls with different wallets in a concise manner:

<<< @./snippets/utilities/using-different-wallet.ts#using-different-wallet{ts:line-numbers}

Changing Providers

Similarly, you can assign a custom provider to a contract instance by modifying its provider property. This enables you to use a provider wrapper of your choice:

const newProvider = new Provider(NEW_URL);
deployedContract.provider = newProvider;

Note: When connecting a different wallet to an existing contract instance, the provider used to deploy the contract takes precedence over the newly set provider. If you have two wallets connected to separate providers (each communicating with a different fuel-core instance), the provider assigned to the deploying wallet will be used for contract calls. This behavior is only relevant when multiple providers (i.e. fuel-core instances) are present and can be ignored otherwise.

Transferring assets

Consider a scenario where you're interacting with a smart contract and need to transfer assets to a recipient's wallet. The addTransfer enables you to combine these actions into a single transaction seamlessly.

The addTransfer method allows you to append an asset transfer to your contract call transaction. You can use it is shown in the following example:

<<< @./snippets/utilities/add-transfer-single-recipient.ts#add-transfer-1{ts:line-numbers}

In the previous example, we first use a contract call to the echo_u64 function. Following this, addTransfer is added to chain call to include a transfer of 100 units of the BaseAssetId in the transaction.

Batch Transfer

You can add a batch of transfers into a single transaction by using addBatchTransfer:

<<< @./snippets/utilities/add-transfer-multiple-recipients.ts#add-transfer-2{ts:line-numbers}

Deploying Contracts

To deploy a contract using the SDK, you can use the ContractFactory. This process involves collecting the contract artifacts, initializing the contract factory, and deploying the contract.

The SDK utilizes two different deployment processes, depending on the contract's size. The threshold for the contract size is dictated by the chain and can be queried:

<<< @./snippets/deploying-contracts/get-max-size.ts#full{ts:line-numbers}

It either uses a single create transaction to deploy the entire contract bytecode, or it splits the contract bytecode into multiple chunks, deploys them as blobs (on chain data accessible to the VM), and then generates a contract from the associated blob IDs. That generated contract is then deployed as a create transaction.

The ContractFactory offers the following methods for the different processes:

  • deploy for deploying contacts of any size (will automatically choose the appropriate deployment process).
  • deployAsCreateTx for deploying the entire contract bytecode in a single create transaction.
  • deployAsBlobTx for deploying the contract in chunks as blobs, and then deploying the contract as a create transaction.

Note: If the contract is deployed via blob deployments, multiple transactions will be required to deploy the contract.

Deploying a Contract Guide

This guide will cover the process of deploying a contract using the deploy method, however all these methods can be used interchangeably dependent on the contract size. In the guide we use a contract factory that has been built using Typegen. This tool provided by the Fuels CLI provides a better developer experience and end to end type support for your smart contracts.

1. Setup

After writing a contract in Sway you can build the necessary deployment artifacts either by running forc build (read more on how to work with Sway) or by using the Fuels CLI and running fuels build using your chosen package manager. We recommend using the Fuels CLI as it provides a more comprehensive usage including end to end type support.

Once you have the contract artifacts, it can be passed to the ContractFactory for deployment, like so:

<<< @./snippets/deploying-contracts/deployment.ts#setup{ts:line-numbers}

2. Contract Deployment

As mentioned earlier, there are two different processes for contract deployment handled by the ContractFactory. These can be used interchangeably, however, the deploy method is recommended as it will automatically choose the appropriate deployment process based on the contract size.

This call resolves as soon as the transaction to deploy the contract is submitted and returns three items: the contractId, a waitForTransactionId function and a waitForResult function.

<<< @./snippets/deploying-contracts/deployment.ts#deploy{ts:line-numbers}

The contract instance will be returned only after calling waitForResult and waiting for it to resolve. To avoid blocking the rest of your code, you can attach this promise to a hook or listener that will use the contract only after it is fully deployed. Similarly, the transaction ID is only available once the underlying transaction has been funded. To avoid blocking the code until the ID is ready, you can use the waitForTransactionId function to await it's retrieval.

3. Executing a Contract Call

Now that the contract is deployed, you can interact with it by submitting a contract call:

<<< @./snippets/deploying-contracts/deployment.ts#call{ts:line-numbers}

Deploying a Large Contract as Blobs

In the above guide we use the recommended deploy method. If you are working with a contract that is too large to be deployed in a single transaction, then the SDK will chunk the contract for you and submit it as blobs, to then be accessed later by a create transaction. This process is handled by the ContractFactory.deployAsBlobTx method.

<<< @./snippets/deploying-contracts/deployment.ts#blobs{ts:line-numbers}

In the above example, we also pass a chunkSizeMultiplier option to the deployment method. The SDK will attempt to chunk the contract to the most optimal about, however the transaction size can fluctuate and you can also be limited by request size limits against the node. By default we set a multiplier of 0.95, meaning the chunk size will be 95% of the potential maximum size, however you can adjust this to suit your needs and ensure the transaction passes. It must be set to a value between 0 and 1.

Note: Deploying large contracts using blob transactions will take more time. Each transaction is dependent and has to wait for a block to be produced before it gets mined. Then a create transaction is submitted as normal. So you will need to wait longer than usual for the contract to be fully deployed and can be interacted with.

Storage Slots

When deploying a contract, you can specify the custom storage slots that you want to use.

<<< @./snippets/storage-slots/override-storage-slots.ts#contract-deployment-storage-slots{ts:line-numbers}

Using plain JavaScript

In the above example, we directly imported the storage slots from a JSON file generated by the Sway compiler.

Instead of importing from a file, you can also specify the custom storage slots directly in your code:

<<< @./snippets/storage-slots/override-storage-slots-inline.ts#contract-deployment-storage-slots-inline{ts:line-numbers}

Auto-load of Storage Slots

Code generated using Typegen automatically load Storage Slots for you.

Configurable Constants

Sway introduces a powerful feature: configurable constants. When creating a contract, you can define constants, each assigned with a default value.

Before deploying the contract, you can then redefine the value for these constants, it can be all of them or as many as you need.

This feature provides flexibility for dynamic contract environments. It allows a high level of customization, leading to more efficient and adaptable smart contracts.

Defining Configurable Constants

Below is an example of a contract in which we declare four configurable constants:

<<< @/../../docs/sway/echo-configurables/src/main.sw#configurable-constants-1{rust:line-numbers}

In this contract, the function echo_configurables returns the values of the configurable constants, which we'll use for demonstrating the setting of configurables via the SDK.

Setting New Values For Configurable Constants

During contract deployment, you can define new values for any/all of the configurable constants. The example below shows setting of one configurable constant, while the others will have default values.

<<< @./snippets/configurable-constants.ts#setting-configurable-constant{ts:line-numbers}

Please note that when assigning new values for a Struct, all properties of the Struct must be defined. Failing to do so will result in an error:

<<< @./snippets/configurable-constants.ts#invalid-configurable{ts:line-numbers}

Minted Token Asset ID

The asset ID of a token on the Fuel network is determined by two factors:

  • The ID of the contract that minted the token,
  • A sub-identifier (Sub ID)

Both of which are B256 strings.

The process involves applying a SHA-256 hash algorithm to the combination of the Contract ID and the Sub ID, to derive an Asset ID - as explained here.

Consider the following simplified token contract:

<<< @/../../docs/sway/token/src/main.sw#minted-token-asset-id-1{rs:line-numbers}

Imagine that this contract is already deployed and we are about to mint some coins:

<<< @./snippets/utilities/minted-token-asset-id.ts#minted-token-asset-id-2{ts:line-numbers}

Obtaining the Asset ID

Since the asset ID depends on the contract ID, which is always dynamic (unlike the sub ID, which can be set to a fixed value), the helper getMintedAssetId can be used to easily obtain the asset ID for a given contract ID and sub ID.

Create Asset Id

The SDK provides a helper named createAssetId which takes the contract ID and sub ID as parameters. This helper internally calls getMintedAssetId and returns the Sway native parameter AssetId, ready to be used in a Sway program invocation:

<<< @./snippets/utilities/create-asset-id.ts#create-asset-id-1{ts:line-numbers}

Managing Deployed Contracts

To interact with a deployed contract using the SDK without redeploying it, you only need the contract ID and its JSON ABI. This allows you to bypass the deployment setup.

Contract ID

The contractId property from the Contract class is an instance of the Address class.

The Address class also provides a set of utility functions for easy manipulation and conversion between address formats along with one property; b256Address, which is a string encoded in B256 format.

When you log the contractId property of an instantiated Contract using console.log, the output appears as follows:

  Address {
    b256Address: '0xcd16d97c5c4e18ee2e8d6428447dd9c8763cb0336718b53652d049f8ec88b3ba'
  }

If you have already an instantiated and deployed contract in hands you can create another contract instance simply by using the contractId property and the contract JSON ABI:

<<< @./snippets/managing-deployed-contracts.ts#with-contractId{ts:line-numbers}

The previous example assumes that you have a Contract instance at hand. However, some Fuel tools and Sway use the B256 type format, a hex-encoded string-like type, for contract IDs.

You might have this format instead, for example, if you have deployed your contract with forc deploy.

The process of instantiating a Contract remains the same when using a contract ID of type B256:

<<< @./snippets/managing-deployed-contracts.ts#with-b256{ts:line-numbers}

Proxy Contracts

Automatic deployment of proxy contracts can be enabled in Forc.toml.

We recommend that you use fuels deploy to deploy and upgrade your contract using a proxy as it will take care of everything for you. However, if you want to deploy a proxy contract manually, you can follow the guide below.

Manually Deploying and Upgrading by Proxy

As mentioned above, we recommend using fuels deploy to deploy and upgrade your contract because it will handle everything automatically. However, the guide below will explain this process in detail if you want to implement it yourself.

We recommend using the SRC14 compliant owned proxy contract as the underlying proxy as that is the one we will use in this guide and the one used by fuels deploy. A TypeScript implementation of this proxy is exported from the fuels package as Src14OwnedProxy and Src14OwnedProxyFactory.

The overall process is as follows:

  1. Deploy your contract
  2. Deploy the proxy contract
  3. Set the target of the proxy contract to your deployed contract
  4. Make calls to the contract via the proxy contract ID
  5. Upgrade the contract by deploying a new version of the contract and updating the target of the proxy contract

Note: When new storage slots are added to the contract, they must be initialized in the proxy contract before they can be read from. This can be done by first writing to the new storage slot in the proxy contract. Failure to do so will result in the transaction being reverted.

For example, lets imagine we want to deploy the following counter contract:

<<< @/../../docs/sway/counter/src/main.sw#proxy-1{rs:line-numbers}

Let's deploy and interact with it by proxy. First let's setup the environment and deploy the counter contract:

<<< @./snippets/proxy-contracts.ts#proxy-2{ts:line-numbers}

Now let's deploy the SRC14 compliant proxy contract and initialize it by setting its target to the counter target ID.

<<< @./snippets/proxy-contracts.ts#proxy-3{ts:line-numbers}

Finally, we can call our counter contract using the contract ID of the proxy.

<<< @./snippets/proxy-contracts.ts#proxy-4{ts:line-numbers}

Now let's make some changes to our initial counter contract by adding an additional storage slot to track the number of increments and a new get method that retrieves its value:

<<< @/../../docs/sway/counter-v2/src/main.sw#proxy-5{rs:line-numbers}

We can deploy it and update the target of the proxy like so:

<<< @./snippets/proxy-contracts.ts#proxy-6{ts:line-numbers}

Then, we can instantiate our upgraded contract via the same proxy contract ID:

<<< @./snippets/proxy-contracts.ts#proxy-7{ts:line-numbers}

For more info, please check these docs:

Understanding the FuelVM Binary File

When you compile your Sway code using the forc build command, it generates a bytecode file. This binary file contains the compiled code that the Fuel Virtual Machine (FuelVM) will interpret and execute.

For example, consider the following smart contract:

<<< @/../../docs/sway/echo-values/src/main.sw#understanding-fuel-binary-file{ts:line-numbers}

After running forc build, a binary file will be generated with the following content:

$ cat out/debug/echo-values.bin
�GT]����]@`I]G�I@sH]G�I@sHr�{6�]D`J]C�%E]@`J$@Ͼ{RD�^�%

At first glance, the content appears unreadable. However, forc provides a helpful interpreter for this bytecode: the forc parse-bytecode command. This command takes the binary data and outputs the equivalent FuelVM assembly:

$ forc parse-bytecode out/debug/echo-values.bin
half-word   byte   op                raw           notes
        0   0      JI(4)             90 00 00 04   jump to byte 16
        1   4      NOOP              47 00 00 00
        2   8      Undefined         00 00 00 00   data section offset lo (0)
        3   12     Undefined         00 00 00 34   data section offset hi (52)
        4   16     LW(63, 12, 1)     5d fc c0 01
        5   20     ADD(63, 63, 12)   10 ff f3 00
        6   24     LW(17, 6, 73)     5d 44 60 49
        7   28     LW(16, 63, 1)     5d 43 f0 01
        8   32     EQ(16, 17, 16)    13 41 14 00
        9   36     JNZI(16, 11)      73 40 00 0b   conditionally jump to byte 44
       10   40     RVRT(0)           36 00 00 00
       11   44     LW(16, 63, 0)     5d 43 f0 00
       12   48     RET(16)           24 40 00 00
       13   52     Undefined         00 00 00 00
       14   56     Undefined         00 00 00 01
       15   60     Undefined         00 00 00 00
       16   64     XOR(20, 27, 53)   21 51 bd 4b

When deploying your smart contract using the SDK, the binary file plays a crucial role. It is sent to the FuelVM in a transaction, allowing the FuelVM to interpret and execute your smart contract.

Scripts

A script, in Sway, is runnable bytecode on the chain which executes once to perform some task. A script can return a single value of any type.

Learn more about scripts here.

Instantiating a script

Similar to contracts and predicates, once you've written a script in Sway and compiled it with forc build (read here for more on how to work with Sway), you'll get the script binary. Using the binary, you can instantiate a script as shown in the code snippet below:

<<< @./snippets/initialising-scripts.ts#script-init{ts:line-numbers}

In the next section, we show how to run a script.

Deploying Scripts

In order to optimize the cost of your recurring script executions, we recommend first deploying your script. This can be done using the Fuels CLI and running the deploy command.

By deploying the script, its bytecode is stored on chain as a blob. The SDK will then produce bytecode that can load the blob on demand to execute the original script. This far reduces the repeat execution cost of the script.

How to Deploy a Script

To deploy a script, we can use the Fuels CLI and execute the deploy command.

This will perform the following actions:

  1. Compile the script using your forc version
  2. Deploy the built script binary to the chain as a blob
  3. Generate a script that loads the blob that can be used to execute the script
  4. Generate types for both the script and the loader that you can use in your application

We can then utilize the above generated types like so:

<<< @./snippets/deploying-scripts.ts#deploying-scripts{ts:line-numbers}

Script With Configurable

In the same way as contracts and predicates, Scripts also support configurable constants. This feature enables dynamic adjustment of certain values within your scripts.

Configurable constants are fairly straightforward to add and set in your scripts.

Let's consider the following script:

<<< @/../../docs/sway/script-sum/src/main.sw#script-with-configurable-contants-1{rust:line-numbers}

In this script, AMOUNT is a configurable constant with a default value of 10. The main function returns the sum of the inputted_amount and the configurable constant AMOUNT.

To change the value of the AMOUNT constant, we can use the setConfigurableConstants method as shown in the following example:

<<< @./snippets/script-with-configurable.ts#script-with-configurable-contants-2{ts:line-numbers}

In this example, we're setting a new value 81 for the AMOUNT constant. We then call the main function with an inputted value of 10.

The expectation is that the script will return the sum of the inputted value and the new value of AMOUNT.

This way, configurable constants in scripts allow for more flexibility and dynamic behavior during execution.

Full Example

For a full example, see below:

<<< @./snippets/script-with-configurable.ts#full{ts:line-numbers}

Running a script

Suppose your Sway script main function is written using the arguments passed to the main function like so:

<<< @/../../docs/sway/script-main-args/src/main.sw#script-with-main-args{rust:line-numbers}

You can still hand code out a solution wrapper using callScript utility to call your script with data. However, if you prefer to use the ABI generated from your script, you can use the ScriptFactory helper:

<<< @./snippets/script-with-main-args.ts#full{ts:line-numbers}

Preparing a Script Transaction

Akin to Contracts, we can configure the call parameters and transaction parameters for Scripts, as well as retrieve the entire transaction request or transaction ID prior to submission.

<<< @./snippets/script-with-configurable.ts#preparing-scripts{ts:line-numbers}

Predicates

Predicates in Sway are specific types of programs that return a boolean value, meaning they function like rules that a transaction must follow to be valid.

They don't have access to the information written on the blockchain – they make decisions based solely on the received parameters.

These predicates are pure functions, which means they don't cause any unintended side effects.

The key difference here is that instead of checking these rules directly on the blockchain, we check them 'off' the blockchain first. Once we're confident they're valid, we then record the transaction on the blockchain.

This method is not only more efficient but also helps to prevent traffic jams on the network and makes transactions cheaper. It does so by reducing the need for repetitive calculations on the blockchain.

Working with Predicates

Users can send assets to the predicate address as they would to any other address on the blockchain. To spend funds stored at the predicate address, users must provide the original byte code of the predicate and, if required, the predicate data.

The predicate data relates to the parameters received by the predicate's main function. This data comes into play during the byte code's execution. If the main function does not have any parameters then there is no data to be provided, therefore we do not provide the predicate data.

If the predicate is validated successfully, the funds will be accessible. Otherwise, the SDK will throw a validation error.

In the next section, we provide a step-by-step guide on how to interact with a predicate to validate your transactions.

Debugging Predicates

Currently there is no way to debug a predicate yet. In the meantime, a practical workaround is to initially write, test, and debug your predicate as a script, which has more debugging tools available. Once it's working as expected, you can then convert it back into a predicate.

Instantiating predicates

A predicate in Sway can be as simple as the following:

<<< @/../../docs/sway/return-true-predicate/src/main.sw#predicate-simple-1{rust:line-numbers}

In this minimal example, the main function does not accept any parameters and simply returns true.

Just like contracts in Sway, once you've created a predicate, you can compile it using forc build. For more information on working with Sway, refer to the Sway documentation.

After compiling, you will obtain the binary of the predicate and its JSON ABI (Application Binary Interface). Using these, you can instantiate a predicate in TypeScript as shown in the code snippet below:

<<< @./snippets/instantiation/simple.ts#predicate-simple-2{ts:line-numbers}

The created Predicate instance, among other things, has three important properties: the predicate bytes (byte code), the chainId, and the predicate address.

This address, generated from the byte code, corresponds to the Pay-to-Script-Hash (P2SH) address used in Bitcoin.

Predicate with multiple arguments

You can pass more than one argument to a predicate. For example, this is a predicate that evaluates to true if the two arguments are not equal:

<<< @/../../docs/sway/predicate-multi-args/src/main.sw#predicate-multi-args-1{rust:line-numbers}

You can pass the two arguments to this predicate like this:

<<< @./snippets/instantiation/multi-args.ts#predicate-multi-args-2{rust:line-numbers}

Predicate with a Struct argument

You can also pass a struct as an argument to a predicate. This is one such predicate that expects a struct as an argument:

<<< @/../../docs/sway/predicate-main-args-struct/src/main.sw#predicate-main-args-struct-1{rust:line-numbers}

You can pass a struct as an argument to this predicate like this:

<<< @./snippets/instantiation/struct-arg.ts#predicate-main-args-struct-2{ts:line-numbers}

Deploying Predicates

In order to optimize the cost of your recurring predicate executions, we recommend first deploying your predicate. This can be done using the Fuels CLI and running the deploy command.

By deploying the predicate, its bytecode is stored on chain as a blob. The SDK will then produce bytecode that can load the blob on demand to execute the original predicate. This far reduces the repeat execution cost of the predicate.

How to Deploy a Predicate

To deploy a predicate, we can use the Fuels CLI and execute the deploy command.

This will perform the following actions:

  1. Compile the predicate using your forc version
  2. Deploy the built predicate binary to the chain as a blob
  3. Generate a new, smaller predicate that loads the deployed predicate's blob
  4. Generate types for both the predicate and the loader that you can use in your application

We can then utilize the above generated types like so:

<<< @./snippets/deploying-predicates.ts#full{ts:line-numbers}

Predicate With Configurable Constants

Predicates, much like contracts and scripts, also supports configurable constants. This enables Predicates to suit specific use cases and enhance their functionality.

Example: Asset Transfer Validation

Let's consider an example where a predicate is used to validate an asset transfer. In this case, the transfer will only be executed if the recipient's address is on a pre-approved whitelist.

The following snippet illustrates how this could be implemented:

<<< @/../../docs/sway/whitelisted-address-predicate/src/main.sw#full{rust:line-numbers}

In this example, you'll notice the use of a configurable constant named WHITELISTED. This constant has a default value that represents the default approved address.

Modifying The Whitelist

If there is a need to whitelist another address, the WHITELISTED constant can be easily updated. The following snippet demonstrates how to set a new value for the WHITELISTED constant and to make the predicate execute the transfer:

<<< @./snippets/configurables/configurable-set-data.ts#full{ts:line-numbers}

By ensuring that the updated WHITELISTED address matches the intended recipient's address, the predicate will validate the transfer successfully.

Default Whitelist Address

In scenarios where the default whitelisted address is already the intended recipient, there's no need to update the WHITELISTED constant. The predicate will validate the transfer based on the default value. Here's how this scenario might look:

<<< @./snippets/configurables/configurable-default.ts#full{ts:line-numbers}

This ability to configure constants within predicates provides a flexible mechanism for customizing their behavior, thereby enhancing the robustness and versatility of our asset transfer process.

It's important to note that these customizations do not directly modify the original predicate. The address of a predicate is a hash of its bytecode. Any change to the bytecode, including altering a constant value, would generate a different bytecode, and thus a different hash. This leads to the creation of a new predicate with a new address.

This doesn't mean that we're changing the behavior of the original predicate. Instead, we're creating a new predicate with a different configuration.

Therefore, while configurable constants do indeed enhance the flexibility and robustness of predicates, it is achieved by creating new predicates with different configurations, rather than altering the behavior of existing ones.

Send And Spend Funds From Predicates

Predicates can be used to validate transactions. This implies that a predicate can safeguard assets, only allowing their transfer if the predicate conditions are met.

This guide will demonstrate how to send and spend funds using a predicate.

Predicate Example

Consider the following predicate:

<<< @/../../docs/sway/simple-predicate/src/main.sw#send-and-spend-funds-from-predicates-1{rust:line-numbers}

This predicate accepts an address of type B256 and compares it with a hard-coded address of the same type. If both addresses are equal, the predicate returns true, otherwise it will return false.

Interacting with the Predicate Using SDK

Let's use the above predicate to validate our transaction.

Once you've compiled the predicate (forc build), you'll obtain two important artifacts: the JSON ABI and the predicate's binary code. These are needed to instantiate a new predicate.

This is where we also pass in the predicate's data. Note that the main function in our predicate example requires a parameter called input_address of type B256. We will pass this parameter to the Predicate constructor along with the bytecode and the JSON ABI.

<<< @./snippets/cookbook/transferring-assets.ts#send-and-spend-funds-from-predicates-2{ts:line-numbers}

Note: If you want to pass in the predicate data after instantiating the Predicate or if you want to use a different data than the one passed in the constructor, you will have to create a new Predicate instance.

With the predicate instantiated, we can transfer funds to its address. This requires us to have a wallet with sufficient funds. If you're unsure about using wallets with the SDK, we recommend checking out our wallet guide.

<<< @./snippets/cookbook/transferring-assets.ts#send-and-spend-funds-from-predicates-3{ts:line-numbers}

Now that our predicate holds funds, we can use it to validate a transaction and hence execute our transfer. We can achieve that by doing the following:

<<< @./snippets/cookbook/transferring-assets.ts#send-and-spend-funds-from-predicates-5{ts:line-numbers}

Note the method transfer has two parameters: the recipient's address and the intended transfer amount.

Once the predicate resolves with a return value true based on its predefined condition, our predicate successfully spends its funds by means of a transfer to a desired wallet.


In a similar approach, you can use the createTransfer method, which returns a ScriptTransactionRequest. Then, we can submit this transaction request by calling the sendTransaction method.

The following example, we are pre-staging a transaction and therefore we are able to know the transaction ID without actually submitting the transaction.

<<< @./snippets/cookbook/pre-stage.ts#send-and-spend-funds-from-predicates-8{ts:line-numbers}

Spending Entire Predicate Held Amount

Trying to forward the entire amount held by the predicate results in an error because no funds are left to cover the transaction fees. Attempting this will result in an error message like:

<<< @./snippets/cookbook/failure-not-enough-funds.ts#send-and-spend-funds-from-predicates-6{ts:line-numbers}

Predicate Validation Failure

What happens when a predicate fails to validate? Recall our predicate only validates if the input_address matches the hard-coded valid_address. Hence, if we set a different data from the valid_address, the predicate will fail to validate.

When a predicate fails to validate, the SDK throws an error that starts like this:

<<< @./snippets/cookbook/failure-returns-false.ts#send-and-spend-funds-from-predicates-7{ts:line-numbers}

Interacting With Predicates

The Predicate class extends the Account class, inheriting all its methods. Therefore, there are multiple ways to interact with predicates, but broadly speaking, we can think about three:

  • Checking Balances
  • Transactions
  • Transfers

Checking Balances

getBalances

This will return the balances of all assets owned by the predicate.

See also: Checking Wallet Balances

getResourcesToSpend

This will return the resources owned by a predicate so that they can be added to a transaction request.

This method is called under the hood when using transfer or createTransfer.

You may want to use this method when using a predicate in an existing transaction request.

<<< @./snippets/methods/get-resources-to-spend.ts#getResourcesToSpend{ts:line-numbers}

Transactions

sendTransaction

This is used to send a transaction to the node.

<<< @./snippets/methods/send-transaction.ts#sendTransaction{ts:line-numbers}

simulateTransaction

You can use the simulateTransaction method to dry-run a predicate call without consuming resources. A typical use case of a dry-run call is to validate that sufficient funds are available to cover the transaction fees.

<<< @./snippets/methods/simulate-transaction.ts#simulateTransaction{ts:line-numbers}

Transfers

createTransfer

The createTransfer method creates a transaction request with all the necessary transfer details. It automatically estimates the transaction costs via a dry-run call and funds the request with the required predicate resources. After this, one can submit the returned transaction request with greater certainty that it will succeed.

However, please remember that you can still modify the transfer request details and use its properties before submitting it to the node.

<<< @./snippets/methods/create-transfer.ts#createTransfer{ts:line-numbers}

transfer

You can send funds to another address using the transfer method.

<<< @./snippets/methods/transfer.ts#transfer{ts:line-numbers}

Custom Transactions

Utilizing predicate logic unlocks a wide range of possibilities for your dApps when creating transactions. Therefore, pairing predicates with custom transactions can help you achieve more complex use cases. This can be achieved by instantiating a custom transaction, appending the predicate resources, and submitting the transaction via a successfully validated predicate.

Custom transactions can be shaped via a ScriptTransactionRequest instance. For more information on crafting custom transactions and the methods available to them, please refer to the Transaction Request guide.

However, this guide will demonstrate how to use a predicate in a custom transaction. Consider the following predicate, where a configurable pin must be used to validate the predicate and unlock the funds:

<<< @/../../docs/sway/configurable-pin/src/main.sw#full{rust:line-numbers}

We can interact with the above and include it in a custom transaction like so:

<<< @./snippets/custom-transactions.ts#predicate-custom-transaction{ts:line-numbers}

Transactions

A transaction is a way of interacting with a Fuel blockchain and can include actions like transferring assets, deploying contracts and minting tokens. All of which are possible through the SDK by using simple utility methods or building out more custom transactions.

Transferring assets is the most common transaction type and can be be executed by calling the transfer function from an account to a recipient address:

<<< @./snippets/transaction.ts#transactions-1{ts:line-numbers}

Deploying and interacting with contracts are other common transactions. More information on this can be found in the contracts guide, either through the contract deployment guide or the contract interaction guide.

This guide will discuss how to create and modify transactions to fit bespoke use cases, as well as submit them to the network using transactional policies and parameters. As well as retrieving information about submitted transactions.

Transaction Request

A transaction request provides the foundations for submitting a transaction and interacting with the blockchain.

Within Fuel, we have the following transaction types:

  • Script
  • Create
  • Mint

The SDK provides class helpers for handling script and create transactions: ScriptTransactionRequest and CreateTransactionRequest, respectively.

Note: Mint transactions can only be created by the block producer and do not have any use outside of block creation. Therefore, the SDK only provides the ability to decode them.

Creating a Transaction Request

To create a transaction request, you must first instantiate either a ScriptTransactionRequest or CreateTransactionRequest.

A ScriptTransactionRequest is used for script transactions, which allows you to execute bytecode on chain to perform a task or chain of tasks. Within the SDK they can be created like so:

<<< @./snippets/transaction-request/create-request.ts#transaction-request-1{ts:line-numbers}

A CreateTransactionRequest is used for create transactions, which are transactions that create a new contract on the blockchain.

<<< @./snippets/transaction-request/create-request.ts#transaction-request-2{ts:line-numbers}

Note: We recommend you use the ContractFactory for contract deployment as this will shape the create transaction request for you. Information on this can be found in the contract deployment guide.

Modifying a Transaction Request

Once you have instantiated a transaction request, you can modify it by setting the transaction parameters and policies. This can either be done manually by directly altering the transaction request object, or through helper methods that are available on the above classes.

Adding OutputCoin

Including OutputCoins in the transaction request specifies the UTXOs that will be created once the transaction is processed. These UTXOs represent the amounts being transferred to specified account addresses during the transaction:

<<< @./snippets/transaction-request/add-output-coin.ts#transaction-request-3{ts:line-numbers}

Estimating and Funding the Transaction Request

Before submitting a transaction, it is essential to ensure it is properly funded to meet its requirements and cover the associated fee. The SDK offers two approaches for this, one is to use the estimateAndFund helper:

<<< @./snippets/transaction-request/estimate-and-fund.ts#estimate-and-fund{ts:line-numbers}

This approach provides a simple one-liner for estimating and funding the transaction request. Ensuring that the gasLimit and maxFee are accurately calculated and that the required amounts for OutputCoins are fulfilled, as well as fetching and adding any missing resources from the calling account.

The other more manual approach is as so:

<<< @./snippets/transaction-request/get-transaction-cost.ts#transaction-request-4{ts:line-numbers}

This approach provides the same behaviour as the estimateAndFund helper, but gives more granular control over the transaction request. The getTransactionCost method also returns various information about the simulated request that you may want to use to further modify the transaction request, more on that can be found in the API reference.

Manually Fetching Resources

In certain scenarios, you may need to manually fetch resources. This can be achieved using the getResourcesToSpend method, which accepts an array of CoinQuantities and returns the necessary resources to meet the specified amounts:

<<< @./snippets/transaction-request/fetch-resources.ts#transaction-request-5{ts:line-numbers}

Manually Fetching Coins or Messages

If needed, you can manually include specific coins or messages in the transaction. However, this approach is generally discouraged and should only be used in scenarios where explicitly adding particular coins or messages to the transaction request is required:

<<< @./snippets/transaction-request/fetch-coins.ts#transaction-request-6{ts:line-numbers}

Adding a Contract Input and Output to a Transaction Request

Imagine that you have a Sway script that manually calls a contract:

<<< @/../../docs/sway/script-call-contract/src/main.sw#transaction-request-7{rs:line-numbers}

In those cases, you will need to add both an InputContract and OutputContract to the transaction request:

<<< @./snippets/transaction-request/input-contract.ts#transaction-request-8{ts:line-numbers}

Adding a Predicate to a Transaction Request

Predicates are used to define the conditions under which a transaction can be executed. Therefore you may want to add a predicate to a transaction request to unlock funds that are utilized by a script. This can be added like so:

<<< @./snippets/transaction-request/add-predicate.ts#transaction-request-9{ts:line-numbers}

Note: For more information on predicates, including information on configuring them, funding them and using them to unlock funds, please refer to the predicate guide.

Adding a Witness and Signing a Transaction Request

The SDK provides a way of either modifying the witnesses for a transaction request directly, or by passing accounts. This will then sign the transaction request with the account's private key. Below will detail how to add a witness to a transaction request:

<<< @./snippets/transaction-request/add-witness.ts#transaction-request-10{ts:line-numbers}

A more complex example of adding multiple witnesses to a transaction request can be seen in the multiple signers guide here, which validates the signatures inside the script itself.

Note: Once addAccountWitnesses has been called, any additional modifications to the transaction request will invalidate the signature as the transaction ID changes. Therefore, it is recommended to add witnesses last.

Getting the Transaction ID for a Transaction Request

The transaction ID is a SHA-256 hash of the entire transaction request. This can be useful for tracking the transaction on chain. To get the transaction ID, you can use the following method:

<<< @./snippets/transaction-request/add-witness.ts#transaction-request-11{ts:line-numbers}

Note: Any changes made to a transaction request will alter the transaction ID. Therefore, you should only get the transaction ID after all modifications have been made.

Burning assets

Assets can be burnt as part of a transaction that has inputs without associated output change. The SDK validates against this behavior, so we need to explicitly enable this by sending the transaction with the enableAssetBurn option set to true.

<<< @./snippets/transaction-request/asset-burn.ts#asset-burn{ts:line-numbers}

Note: Burning assets is permanent and all assets burnt will be lost. Therefore, be mindful of the usage of this functionality.

Adding Parameters

Transaction parameters allow you to configure various aspects of your blockchain transactions. Dependent on these parameters, it may introduce a transaction policy.

All available parameters are shown below:

<<< @./snippets/transaction-parameters.ts#transaction-parameters-7{ts:line-numbers}

Gas Limit

The maximum amount of gas you're willing to allow the transaction to consume. If the transaction requires more gas than this limit, it will fail.

<<< @./snippets/transaction-parameters.ts#transaction-parameters-1{ts:line-numbers}

Max Fee

The maximum amount you're willing to pay for the transaction using the base asset. This allows users to set an upper limit on the transaction fee they are willing to pay, preventing unexpected high costs due to sudden network congestion or fee spikes.

<<< @./snippets/transaction-parameters.ts#transaction-parameters-2{ts:line-numbers}

Tip

An optional amount of the base asset to incentivise the block producer to include the transaction, ensuring faster processing for those willing to pay more. The value set here will be added to the transaction maxFee.

<<< @./snippets/transaction-parameters.ts#transaction-parameters-3{ts:line-numbers}

Maturity

The number of blocks that must pass before the transaction can be included in a block. This is useful for time-sensitive transactions, such as those involving time-locked assets.

For example, if the chain produces a new block every second, setting Maturity to 10 means the transaction will be processed after approximately 10 seconds.

<<< @./snippets/transaction-parameters.ts#transaction-parameters-4{ts:line-numbers}

Witness Limit

The maximum byte length allowed for the transaction witnesses array. For instance, imagine a transaction that will deploy a contract. The contract bytecode will be one of the entries in the transaction witnesses. If you set this limit to 5000 and the contract bytecode length is 6000, the transaction will be rejected because the witnesses bytes length exceeds the maximum value set.

<<< @./snippets/transaction-parameters.ts#transaction-parameters-5{ts:line-numbers}

Expiration

The block number after which the transaction can no longer be included in the blockchain. For example, if you set the expiration block for your transaction to 200, and the transaction remains in the queue waiting to be processed when block 200 is created, the transaction will be rejected.

<<< @./snippets/transaction-parameters.ts#transaction-parameters-6{ts:line-numbers}

Variable Outputs

The number of variable outputs that should be added to the transaction request. You can read more about it on this guide

Note: Setting transaction parameters is optional. If you don't specify them, the SDK will fetch some sensible defaults from the chain.

Setting Transaction Parameters

To set the transaction parameters, you have access to the txParams method on a transaction request.

<<< @./snippets/transaction-parameters.ts#transaction-parameters-8{ts:line-numbers}

The same method is also accessible within a function invocation scope, so it can also be used when calling contract functions.

<<< @./snippets/transaction-parameters.ts#transaction-parameters-9{ts:line-numbers}

Note: When performing an action that results in a transaction (e.g. contract deployment, contract call with .call(), asset transfer), the SDK will automatically estimate the fee based on the gas limit and the transaction's byte size. This estimation is used when building the transaction. As a side effect, your wallet must own at least one coin of the base asset, regardless of the amount.

Full Example

Here is the full example of the transaction parameters:

<<< @./snippets/transaction-parameters.ts#full{ts:line-numbers}

Adding Policies

Transaction policies are rules that can govern how a transaction is processed, introduced by the transaction parameters that you pass to a transaction request. The available policies are as follows:

Tip

Optional amount on the base asset to incentivise block producer to include transaction, ensuring faster processing for those willing to pay more. The value set here will be added to the transaction maxFee.

Witness Limit

The maximum byte length allowed for the transaction witnesses array.

Maturity

The number of blocks that must pass before the transaction can be included in a block.

Max Fee

The maximum amount you're willing to pay for the transaction using the base asset.

Expiration

Block number after which the transaction can no longer be included in the blockchain.

Setting Transaction Policies

The following snippet shows which transaction parameters correspond to which policies:

<<< @./snippets/transaction-policies/setting-policies.ts#transaction-policies-1{ts:line-numbers}

Retrieving Transaction Policies from a Transaction

Policies used for a transaction can be retrieved from a transaction using a TransactionResponse. The below snippet will show how to retrieve the policies from a transaction:

<<< @./snippets/transaction-policies/policies-from-response.ts#transaction-policies-2{ts:line-numbers}

Transaction Response

Once a transaction has been submitted, you may want to extract information regarding the result of the transaction. The SDK offers a TransactionResponse class with helper methods to expose the following information:

  • The transaction ID
  • The status (submitted, success, squeezed out, or failure)
  • Receipts (return data, logs, mints/burns, transfers and panic/reverts)
  • Operations (contract calls, transfers, withdrawals)
  • Gas fees and usages
  • Date and time of the transaction
  • The block the transaction was included in

We can easily extract this information from a contract call:

<<< @./snippets/transaction-response/contract-call.ts#transaction-response-1{ts:line-numbers}

We can also use the result of a transaction request to extract a transaction summary:

<<< @./snippets/transaction-response/from-submitted-request.ts#transaction-response-2{ts:line-numbers}

Or we can build a transaction summary from a stored transaction ID:

<<< @./snippets/transaction-response/from-submitted-request.ts#transaction-response-3{ts:line-numbers}

Optimizing Frontend Apps

Your application must perform a series of operations to estimate, submit and receive the result of a transaction. However, the flow in which it performs these actions can be organized or performed optimistically, increasing it's perceived speed.

Use Case

In a frontend application, imagine we have a button that executes a contract call:

<Button onClick={handleSubmit}>Submit</Button>

The handler would be implemented as follows:

<<< @./snippets/transaction-speed/transaction-speed-init.ts#main{ts:line-numbers}

Once the user clicks the button, multiple sequential calls are made to the network, which can take a while because the transaction must be:

  1. Estimated
  2. Funded
  3. Submitted

Optimization Strategy

With a few optimizations, the flow can be organized as follows:

<<< @./snippets/transaction-speed/transaction-speed-optimized.ts#main{ts:line-numbers}

Conclusion

Finally, when users click the button, they only need to submit the transaction, which vastly improves the perceived speed of the transaction because many of the necessary requests were done upfront, under the hood.

Just remember:

  • After preparation, any changes made to the transaction request will require it to be re-estimated and re-funded before it can be signed and submitted.

See Also

Encoding

Encoding is the process of serializing data into a format that is suitable for transmission or storage. This is important for blockchains as it enables state minimization and efficiency, as well as for generating proofs.

Fortunately, if you are working with program types on the Fuel network such as calling contracts, the SDK will handle encoding for you automatically. It will adhere to the argument encoding specification, so you can work with data in its expected format rather than as bytecode.

However, there may be scenarios where you want to manipulate call or return data yourself or even implement your own serialization specification. This guide will cover how to encode and decode data using the SDK.

Encode and Decode

To interact with the FuelVM, types must be encoded and decoded per the argument encoding specification. The SDK provides the Interface class to encode and decode data.

The relevant methods of Interface are:

  • encodeType
  • decodeType

The Interface class requires you to pass the ABI on initialization. Both methods accept a concreteTypeId, which must exist in the ABI's concreteTypes array. After that, a suitable coder will be assigned to encode/decode that type.

Imagine we are working with the following script that returns the sum of two u32 integers:

<<< @/../../docs/sway/script-sum/src/main.sw#encode-and-decode-1{rust:line-numbers}

When you build this script, using:

forc build

It will produce the following ABI:

<<< @./snippets/encode-and-decode.jsonc#encode-and-decode-2{json:line-numbers}

Now, let's prepare some data to pass to the main function to retrieve the combined integer. The function expects and returns a u32 integer. So here, we will encode the u32 to pass it to the function and receive the same u32 back, as bytes, that we'll use for decoding. We can do both of these with the Interface.

First, let's prepare the transaction:

<<< @./snippets/encode-and-decode.ts#encode-and-decode-3{ts:line-numbers}

Now, we can encode the script data to use in the transaction:

<<< @./snippets/encode-and-decode.ts#encode-and-decode-4{ts:line-numbers}

Finally, we can decode the result:

<<< @./snippets/encode-and-decode.ts#encode-and-decode-5{ts:line-numbers}

A similar approach can be taken with Predicates; however, you must set the encoded values to the predicateData property.

Contracts require more care. Although you can utilize the scriptData property, the arguments must be encoded as part of the contract call script. Therefore, it is recommended to use a FunctionInvocationScope when working with contracts which will be instantiated for you when submitting a contract function, and therefore handles all the encoding.

Full Example

Here is the full example of the encoding and decoding methods:

<<< @./snippets/encode-and-decode.ts#full{ts:line-numbers}

Working with Bytes

This guide aims to give a high-level overview of how to work with bytes in the SDK and the structures we expect them to take. For a complete overview of ABI Encoding generally, within the Fuel network, we recommend you see the ABI Encoding documentation.

Core Types

We know the sizes of all core types at compile time. They are the building blocks of the more complex types and are the most common types you will encounter.

Unsigned Integer (u8 / u16 / u32 / u64 / u128 / u256)

Each type will only contain the number of bits specified in the name. For example, a u8 will contain 8 bits, and a u256 will contain 256 bits and take up the exact property space with no additional padding.

<<< @./snippets/working-with-bytes.ts#working-with-bytes-1{ts:line-numbers}

Boolean

A boolean is encoded as a single byte like a u8, its value being either 0 or 1.

<<< @./snippets/working-with-bytes.ts#working-with-bytes-2{ts:line-numbers}

Fixed Length String

A fixed-length string's size is known at compile time due to the argument declaration of str[n] with n denoting its length. Each character in the string is encoded as a utf-8 bit.

<<< @./snippets/working-with-bytes.ts#working-with-bytes-3{ts:line-numbers}

B256 / B512

These are fixed-length byte arrays, with B256 containing 256 bits and B512 containing 512 bits. You can use them for address and signature formats.

<<< @./snippets/working-with-bytes.ts#working-with-bytes-4{ts:line-numbers}

Automatically Encoded Types

These are the types that will contain nested types and no additional encoding is required other than the encoding of the nested types. This is relevant to arrays, tuples, and structs and enums. The only caveat here, is an enum will also contain a u64 representing the enum case value. options are encoded in the same way as enums.

<<< @./snippets/working-with-bytes.ts#working-with-bytes-5{ts:line-numbers}

Heap types

Heap types are types with a dynamic length that we do not know at compile time. These are Vec, String, and raw_slice. These types are encoded with a u64 representing the length of the data, followed by the data itself.

<<< @./snippets/working-with-bytes.ts#working-with-bytes-6{ts:line-numbers}

Full Example

Here is the full example of the working with bytes functions:

<<< @./snippets/working-with-bytes.ts#full{ts:line-numbers}

Utilities

Utilities are a set of helpers that can be used for various purposes.

Date conversion

To allow for easier manipulation of date and time, the SDK exports the DateTime class which is a wrapper around the built-in Date class. Below we will go over the methods of instantiation, utility functions and time formats.

Internally the transactions and other time/date assets are encoded using the TAI64 format. We return a DateTime class, to allow of easier conversion and formatting between the two formats.

Instantiating a DateTime

We have a host of static method for instantiation of our DateTime class.

<<< @./snippets/date-conversion/instantiation.ts#create-from-multiple-sources{ts:line-numbers}

TAI64

fromTai64 is a static method, that allows the creation of DateTime class from a TAI64 string.

toTai64 is an instance method, that allows the conversion of a DateTime class to a TAI64 string.

<<< @./snippets/date-conversion/tai64.ts#from-tai-64-and-to-tai-64{ts:line-numbers}

UNIX

fromUnixMilliseconds is a static method, that allows the creation of DateTime class from a UNIX Milliseconds number.

toUnixMilliseconds is an instance method, that allows the conversion of a DateTime class to a UNIX number in milliseconds.

<<< @./snippets/date-conversion/unix-milliseconds.ts#from-unix-milliseconds-and-to-unix-milliseconds{ts:line-numbers}

fromUnixSeconds is a static method, that allows the creation of DateTime class from a UNIX Seconds number.

toUnixSeconds is an instance method, that allows the conversion of a DateTime class to a UNIX number in seconds.

<<< @./snippets/date-conversion/unix-seconds.ts#from-unix-seconds-and-to-unix-seconds{ts:line-numbers}

Date

The DateTime class extends the functionality of the Date object, so all method are available for your usages.

<<< @./snippets/date-conversion/date-object.ts#date-object-methods{ts:line-numbers}

Formats

Here we will go over the different date/time formats that we use in the SDK. Internally the blockchain uses the TAI64 format, but we also support the UNIX format for ease of use.

UNIX Format

UNIX time is the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, minus leap seconds. Every day is treated as if it contains exactly 86400 seconds, so leap seconds are ignored.

TAI Format

TAI stands for Temps Atomique International and is the current international real-time standard Source.

We use TAI64 is a 64-bit integer representing the number of nanoseconds since the epoch.

  • the TAI second beginning exactly (2^62 - s) seconds before the beginning of 1970 TAI, if s is between 0 inclusive and 2^62 exclusive; or

  • the TAI second beginning exactly (2^62 + s) seconds after the beginning of 1970 TAI, if s is between -2^62 inclusive and 0 exclusive.

Source

Address

Addresses and varying address formats are commonplace when interacting with decentralized applications. Furthermore, different networks may enforce different address formats.

The Fuel Network uses the B256 address format for its interactions, an example of which can be seen below:

<<< @/../../docs/src/guide/types/snippets/b256.ts#addresses-1{ts:line-numbers}

However, a hexlified B256 (hex) is another common address format; an example can be seen below:

<<< @/../../docs/src/guide/types/snippets/evm-address/creating-an-evm.ts#snippet-2{ts:line-numbers} apps/

At times, these can even be wrapped in a Struct. Such as an Asset ID or a EVM Address:

<<< @/../../docs/src/guide/types/snippets//evm-address/using-an-evm-address-1.ts#snippet-2{ts:line-numbers}

The TS-SDK makes converting between these addresses simple using the Address helper, which provides various utilities for conversion.

The following conversion guide will show how to utilize this class to convert between address formats, as well as Sway Standard Types.

Address Conversion

This guide demonstrates how to convert between address formats and Sway Standard Types using helper functions. Native types are wrappers for bytes, and you can perform conversions between them by leveraging these functions and classes.

Converting a Contract ID

The Contract id property is an instance of the Address class. Therefore, it can be converted using the Address class functions such as toAddress and toB256:

<<< @./snippets/address-conversion/contract.ts#conversion-2{ts:line-numbers}

Converting a Wallet Address

Similarly, the Wallet address property is also of type Address and can therefore use the same Address class functions for conversion:

<<< @./snippets/address-conversion/wallet.ts#conversion-3{ts:line-numbers}

Converting an Asset ID

Asset IDs are a wrapped B256 value. The following example shows how to create an Address from a B256 type:

<<< @./snippets/address-conversion/asset-id.ts#conversion-4{ts:line-numbers}

Unit conversion

Internally, we use Arbitrary-precision arithmetic (also known as Big Number arithmetic) to allow for the handling of large numbers and different assets.

On the Fuel network, we work with 9 decimals to represent amounts under a unit. This differs from chain to chain, so it is important to know the number of decimals used on the chain you are working with.

Note: The package @fuels/assets provides a list of assets and their decimals.

Below we will go over some common use cases for unit conversion.

Using our BN class we can instantiate these numbers.

<<< @./snippets/unit-conversion.ts#instantiation-1{ts:line-numbers}

Or using our bn utility function.

<<< @./snippets/unit-conversion.ts#instantiation-2{ts:line-numbers}

Contract calls

Generally, we will need to convert u64 and u256 numbers to a BN object when passing them to a Sway program from JavaScript. More information on this can be found here.

<<< @./snippets/unit-conversion.ts#contract-calls-1{ts:line-numbers}

Note: If a contract call returns a number that is too large to be represented as a JavaScript number, you can convert it to a string using the toString method instead of toNumber.

Parsing

Parsing string-represented numbers (from user input) has never been easier, than using the parseUnits function.

<<< @./snippets/unit-conversion.ts#parse-units-1{ts:line-numbers}

We can parse large numbers.

<<< @./snippets/unit-conversion.ts#parse-units-2{ts:line-numbers}

Or numbers formatted for human readability.

<<< @./snippets/unit-conversion.ts#parse-units-3{ts:line-numbers}

We can also parse numbers in other units of measure.

<<< @./snippets/unit-conversion.ts#parse-units-4{ts:line-numbers}

Formatting

We can format common units of measure using the format function.

In the following example, we format a BigNumber representation of one Gwei, into units for the Fuel network (with 3 decimal place precision).

<<< @./snippets/unit-conversion.ts#format-1{ts:line-numbers}

We can also format numbers in other units of measure by specifying the units variable.

<<< @./snippets/unit-conversion.ts#format-2{ts:line-numbers}

A precision variable will allow for the formatting of numbers with a specific number of decimal places.

<<< @./snippets/unit-conversion.ts#format-3{ts:line-numbers}

Format units

The formatUnits function is a lesser alternative to the format function, as it will maintain the same precision as the input value.

<<< @./snippets/unit-conversion.ts#format-units-1{ts:line-numbers}

We can also format numbers in other units of measure by specifying the units variable.

<<< @./snippets/unit-conversion.ts#format-units-2{ts:line-numbers}

See also

Full Example

For the full example of unit conversion see the snippet below:

<<< @./snippets/unit-conversion.ts#full{ts:line-numbers}

Assets

We export an array of Asset objects, that can be useful when creating your dApp. The Asset object has useful metadata about the different assets that are available on blockchain networks (Fuel and Ethereum).

Included assets such as:

  • Ethereum (ETH)
  • Tether (USDT)
  • USD Coin (USDC)
  • Wrapped ETH (WETH)

The helper functions getAssetFuel and getAssetEth can be used to get an asset's details relative to each network. These return a combination of the asset, and network information (the return types are AssetFuel and AssetEth respectively).

<<< @./snippets/using-assets.ts#using-assets-1{ts:line-numbers}

Asset API

The Asset API is a RESTful API that allows you to query the assets on the Fuel blockchain. We allow for querying the Asset API on both the Mainnet and Testnet.

Endpoint
Mainnethttps://mainnet-explorer.fuel.network
Testnethttps://explorer-indexer-testnet.fuel.network

For more information about the API, please refer to the Wiki page.

Asset by ID

We can request information about an asset by its asset ID, using the getAssetById function. This will leverage the endpoint /assets/<assetId> to fetch the asset information.

<<< @./snippets/asset-api/asset-by-id.ts#full{ts:line-numbers}

By default, we will request the asset information for mainnet. If you want to request the asset information from other networks, you can pass the network parameter (this is the same for the getAssetsByOwner function).

<<< @./snippets/asset-api/asset-by-id.ts#testnet{ts:line-numbers}

Assets by Owner

We can request information about an asset by its owner, using the getAssetsByOwner function. This will leverage the endpoint /accounts/<owner>/assets to fetch the asset information.

<<< @./snippets/asset-api/assets-by-owner.ts#full{ts:line-numbers}

You can change the pagination parameters to fetch more assets (up to 100 assets per request).

<<< @./snippets/asset-api/assets-by-owner.ts#pagination{ts:line-numbers}

Cookbook

This section covers more advanced use cases that can be satisfied by combining various features of the SDK. As such, it assumes that you have already made yourself familiar with the previous chapters of this book.

Deposit And Withdraw

Consider the following contract:

<<< @/../../docs/sway/liquidity-pool/src/main.sw#deposit-and-withdraw-cookbook-1{rust:line-numbers}

As the name implies, this contract represents a simplified version of a liquidity pool. The deposit() method allows you to supply an arbitrary amount of BASE_TOKEN. In response, it mints twice the amount of the liquidity asset to the caller's address. Similarly, the withdraw() method transfers half the amount of the BASE_TOKEN back to the caller's address.

Now, let's deposit some tokens into the liquidity pool contract. Since this requires forwarding assets to the contract, we need to pass the appropriate values to callParams when creating a contract call.

<<< @./snippets/deposit-and-withdraw/deposit.ts#deposit-and-withdraw-cookbook-2{ts:line-numbers}

As a final demonstration, let's use all our liquidity asset balance to withdraw from the pool and confirm we retrieved the initial amount. For this, we get our liquidity asset balance and supply it to the withdraw() function via callParams.

<<< @./snippets/deposit-and-withdraw/withdraw.ts#deposit-and-withdraw-cookbook-3{ts:line-numbers}

Wallet SDK and React Hooks

This guide will show you how you can use the Fuel Wallet SDK and its React Hooks to build a simple React application that lets users connect their wallet to your application and see their balance.

Setup

The first thing we will do is setup a Next.js project.

::: code-group

npm create next-app my-fuel-app
pnpm create next-app my-fuel-app
bun create next-app my-fuel-app

:::

Next, we will install the Fuel Wallet React SDK and the Fuel TypeScript SDK.

::: code-group

npm install fuels @fuels/connectors @fuels/react @tanstack/react-query
pnpm add fuels @fuels/connectors @fuels/react @tanstack/react-query
bun add fuels @fuels/connectors @fuels/react @tanstack/react-query

:::

The Provider

In order to make use of the React hooks provided by the Fuel Wallet SDK, we need to wrap our application in a FuelProvider component. This component will provide the hooks with the necessary context to interact with the Fuel Wallet SDK. Add the following to your pages/_app.tsx file:

<<< @/../../demo-wallet-sdk-react/src/app/layout.tsx#wallet-sdk-react-provider{tsx:line-numbers}

Building the UI

Go to your pages/index.tsx file and replace the contents with the following:

<<< @/../../demo-wallet-sdk-react/src/app/page.tsx#wallet-sdk-react-ui{tsx:line-numbers}

Let's break down what's happening here.

The useConnectors hook returns a list of available wallet connectors.

Once a connector has been selected by the user, the useConnect hook will return a connect function that can be used to connect the user's wallet to your application.

The useAccount hook returns information about the user's account, if they are connected.

The useBalance hook returns the user's ETH balance on the testnet network, if they are connected.

Further Reading

Custom Transactions

There may be scenarios where you need to build out transactions that involve multiple program types and assets; this can be done by instantiating a ScriptTransactionRequest. This class allows you to a append multiple program types and assets to a single transaction.

Consider the following script that transfers multiple assets to a contract:

<<< @/../../docs/sway/script-transfer-to-contract/src/main.sw#custom-transactions-1{rust:line-numbers}

This script can be executed by creating a ScriptTransactionRequest, appending the resource and contract inputs/outputs and then sending the transaction, as follows:

<<< @/../../docs/src/guide/scripts/snippets/script-custom-transaction.ts#custom-transactions-2{ts:line-numbers}

Full Example

For a full example, see below:

<<< @/../../docs/src/guide/scripts/snippets/script-custom-transaction.ts#full{ts:line-numbers}

Custom Transactions From Contract Calls

In the previous example we demonstrated how you can instantiate a ScriptTransactionRequest to customize and build out a more complex transaction via a script. The same can be done using contracts, but this allows us to utilize functions available in the contract and access on-chain state. Allowing us to harness all of the power from an invocation scope and a transaction request.

This cookbook demonstrates how we can utilize a contract call to build out a custom transaction, allowing us to update on-chain state and transfer assets to a recipient address.

<<< @./snippets/custom-contract-calls.ts#custom-transactions-contract-calls{ts:line-numbers}

Generate Fake Resources

When working with an unfunded account, you can generate fake resources to perform a dry-run on your transactions. This is useful for testing purposes without the need for real funds.

Below is an example script that returns the value 1337. You can use fake resources to execute a dry-run of this script and obtain the returned value.

<<< @/../../docs/sway/return-script/src/main.sw#generate-fake-resources-1{rust:line-numbers}

To execute a dry-run, use the Provider.dryRun method. Ensure you set the utxo_validation flag to true, as this script uses fake UTXOs:

<<< @./snippets/fake-resources.ts#generate-fake-resources-2{ts:line-numbers}

By setting utxo_validation to true, you can successfully execute the dry-run and retrieve the returned value from the script without requiring actual funds.

Transactions with Multiple Signers

When a transaction contains a spendable input such as a coin, it must also contain the signature of the coin owner for it to be spent. If the coin owner is also submitting the transaction, then this is straightforward. However, if an external address is required to sign the transaction, then the transaction must contain multiple signatures. Within the SDK, an account signature can be added to a transaction by calling addAccountWitnesses on the transaction request.

Consider a script that requires two signatures to be spent:

<<< @/../../docs/sway/script-signing/src/main.sw#multiple-signers-1{rust:line-numbers}

In the snippet above, we use the built-in Sway function tx_witness_data() to retrieve the witness signatures and tx_id() for the transaction hash. Then, we retrieve the signing address to validate the script.

We would interact with this script in the SDK by creating a transaction request from an invocation scope. The same can be done for a contract. Consider the following script:

<<< @./snippets/signing-transactions/script.ts#multiple-signers-2{ts:line-numbers}

The same approach can be used for a predicate by instantiating it and adding it to a transaction request. Consider the following predicate:

<<< @/../../docs/sway/predicate-signing/src/main.sw#multiple-signers-3{rust:line-numbers}

We can interact with this predicate in the SDK with the following implementation:

<<< @./snippets/signing-transactions/predicate.ts#multiple-signers-4{ts:line-numbers}

GraphQL Integration

The Fuel Network provides a GraphQL API to query the blockchain. To get a better understanding of the underlying schema and other operations, you can visit the playground for an interactive deep dive.

Operations

For its own purposes, the SDK creates custom operations based off of the API's schema and auto-generates TypeScript client code via codegen tools. The end result of this code generation are the operations available on the Provider, of which some are shown below:

<<< @/../../docs/src/guide/provider/snippets/provider-operations.ts#operations{ts:line-numbers}

Note that these operations primarily serve the needs of the SDK and the Provider's methods which can encapsulate calls to multiple operations, parse the responses, etc.

If your querying needs exceed what the Provider provides, we suggest you follow this same process and write your own custom query operations, e.g.:

query getChain {
  latestBlock {
    transactions {
      id
    }
  }
}

Mutations and subscriptions

For mutations and subscriptions, we strongly suggest that you communicate with the node via the Provider and do not write your own custom GraphQL operations because, in its methods, the Provider does additional processing before and after sending them to the node which might require detailed knowledge of various Fuel domain-specific topics.

Resubmitting Failed Transactions

In certain scenarios, you might need to implement a solution to resubmit failed transactions to the Fuel Network. While this approach can be effective, there are important considerations to remember.

Submission and Processing

When submitting a transaction, you will first get a response.

<<< @./snippets/resubmitting-failed-transactions/submitting.ts#resubmitting-failed-transactions-1{ts:line-numbers}

If the sendTransaction method resolves without an error, we know that the transaction was successfully submitted and accepted by the network. However, this does not guarantee that the transaction has been processed; it only indicates that the transaction has been accepted and placed in a queue for processing.

To determine whether the transaction has been processed, you must call waitForResult, which will either resolve (with the processed transaction) or reject with an error.

<<< @./snippets/resubmitting-failed-transactions/submitting.ts#resubmitting-failed-transactions-2{ts:line-numbers}

In other words:

  • If sendTransaction is rejected with an error, the transaction was not accepted by the network and is not processed.
  • If waitForResult is rejected with an error, the transaction was accepted but reverted during processing.

Resources Spent When a Transaction Is Processed

If a transaction is reverted during processing, the Fuel VM will still consume the funded resources to cover the gas used up to the point of failure. After deducting the gas cost, the remaining funds will be added to a new UTXO (Unspent Transaction Output) addressed to the owner.

Attempting to resubmit the same transaction request that failed during processing will likely result in an error, as the initially spent resources no longer exist.

<<< @./snippets/resubmitting-failed-transactions/wrong-resubmission.ts#resubmitting-failed-transactions-3{ts:line-numbers}

The attempt from the above snippet will result in the error:

FuelError: Transaction is not inserted. UTXO does not exist: {{utxoId}}

To safely retry a transaction that failed during processing, you should reassemble the request from scratch and resubmit it.

<<< @./snippets/resubmitting-failed-transactions/right-resubmission.ts#resubmitting-failed-transactions-4{ts:line-numbers}

Combining UTXOs

When performing a funding operation or calling getResourcesToSpend, you may encounter the INSUFFICIENT_FUNDS_OR_MAX_COINS error if the number of coins fetched per asset exceeds the maximum limit allowed by the chain.

You may also want to do this if you want to reduce the number of inputs in your transaction, which can be useful if you are trying to reduce the size of your transaction or you are receiving the MAX_INPUTS_EXCEEDED error.

One way to avoid these errors is to combine your UTXOs. This can be done by performing a transfer that combines multiple UTXOs into a single UTXO, where the transaction has multiple inputs for the asset, but a smaller number of outputs.

Note: You will not be able to have a single UTXO for the base asset after combining, as one output will be for the transfer, and you will have another for the fees.

<<< @./snippets/combining-utxos.ts#combining-utxos{ts:line-numbers}

Max Inputs and Outputs

It's also important to note that depending on the chain configuration, you may be limited on the number of inputs and/or outputs that you can have in a transaction. These amounts can be queried via the TxParameters GraphQL query.

<<< @./snippets/max-outputs.ts#max-outputs{ts:line-numbers}

Splitting UTXOs

There may be times when you want to split one large UTXO into multiple smaller UTXOs. This can be useful if you want to send multiple concurrent transactions without having to wait for them to be processed sequentially.

Note: Depending on how many smaller UTXOs you want to create, you may need to fund the wallet with additional funds to cover the fees. As we see in the example below, we fund the sending wallet with 500 to cover the fees for the batch transfer.

<<< @./snippets/splitting-utxos.ts#splitting-utxos{ts:line-numbers}

Optimized React Example

This example implements the strategies outlined in Optimizing Frontend Apps and demonstrates how to improve the perceived speed of transactions in a React application.

import { Provider, Wallet, ScriptTransactionRequest } from "fuels";
import { useEffect, useState } from "react";

import { TestContract } from "./typegend";
import contractIds from "./typegend/contract-ids.json";

function App() {
  const [request, setRequest] = useState<ScriptTransactionRequest | null>(null);

  // Initialize the provider and wallet
  const NETWORK_URL = "https://mainnet.fuel.network/v1/graphql";
  const provider = new Provider(NETWORK_URL);
  const wallet = Wallet.fromAddress("0x...", provider);

  /**
   * Here we'll prepare our transaction upfront on page load, so that
   * by the time the user interacts with your app (i.e. clicking a btn),
   * the transaction is ready to be submitted
   */
  useEffect(() => {
    const onPageLoad = async () => {
      // 1. Connect to the contract
      const contractInstance = new TestContract(
        contractIds.testContract,
        wallet,
      );

      // 2. Invoke the contract function whilst estimating and funding the
      // call, which gives us the transaction request
      const preparedRequest = await contractInstance.functions
        .increment_counter(1)
        .fundWithRequiredCoins();

      setRequest(preparedRequest);
    };

    onPageLoad();
  }, []);

  /**
   * By the time user user clicks the submit button, we only need to
   * submit the transaction to the network
   */
  const handleSubmit = async () => {
    if (!request) return;

    // 1. Submit the transaction to the network
    const response = await wallet.sendTransaction(request);

    // 2. Wait for the transaction to settle and get the result
    const result = await response.waitForResult();

    console.log("result", result);
  };

  return (
    <div>
      <button onClick={handleSubmit}>Submit</button>
    </div>
  );
}

export default App;

Testing

This guide will teach you how to test Sway applications using the Typescript SDK.

While we use Vitest internally, we don't enforce any specific testing library or framework, so you can pick whichever you feel comfortable with.

Not using Typescript?

See also:

  1. Using forc test
  2. Using the Rust SDK

Launching a Test Node

To simplify testing in isolation, we provide a utility called launchTestNode.

It allows you to spin up a short-lived fuel-core node, set up a custom provider, wallets, deploy contracts, and much more in one go.

For usage information for launchTestNode including it's inputs, outputs and options, please check the API reference.

Explicit Resource Management

We support explicit resource management, introduced in TypeScript 5.2, which automatically calls a cleanup function after a variable instantiated with the using keyword goes out of block scope:

<<< @./snippets/launching-a-test-node.ts#automatic-cleanup{ts:line-numbers}

Configuring Typescript

To use explicit resource management, you must:

  1. Set your TypeScript version to 5.2 or above
  2. Set the compilation target to es2022 or below
  3. Configure your lib setting to either include esnext or esnext.disposable
{
  "compilerOptions": {
    "target": "es2022",
    "lib": ["es2022", "esnext.disposable"]
  }
}

Standard API

If you don't want, or can't use explicit resource management, you can use const as usual.

In this case, remember you must call .cleanup() to dispose of the node.

<<< @./snippets/launching-a-test-node.ts#manual-cleanup{ts:line-numbers}

Test Node Options

This reference describes all the options of the launchTestNode utility:

<<< @./snippets/launching-a-test-node.ts#options{ts:line-numbers}

Check out the API reference for usage information on the Test Node Options.

walletsConfig

Used to set the node's genesis block state (coins and messages).

  • count: number of wallets/addresses to generate on the genesis block.
  • assets: configure how many unique assets each wallet will own with the base asset included. Can be number or TestAssetId[].
    • The TestAssetId utility simplifies testing when different assets are necessary.
  • coinsPerAsset: number of coins (UTXOs) per asset id.
  • amountPerCoin: for each coin, the amount it'll contain.
  • messages: messages to assign to the wallets.

walletsConfig.assets

The TestAssetId utility integrates with walletsConfig and gives you an easy way to generate multiple random asset ids via the TestAssetId.random static method.

<<< @./snippets/launching-a-test-node.ts#asset-ids{ts:line-numbers}

walletsConfig.messages

The TestMessage helper class is used to create messages for testing purposes. When passed via walletsConfig.messages, the recipient field of the message is overriden to be the wallet's address.

<<< @./snippets/launching-a-test-node.ts#test-messages{ts:line-numbers}

It can also be used standalone and passed into the initial state of the chain via the TestMessage.toChainMessage instance method.

<<< @./snippets/launching-a-test-node.ts#test-messages-chain{ts:line-numbers}

contractsConfigs

Used to deploy contracts on the node the launchTestNode utility launches. It's an array of objects with the following properties:

nodeOptions

Options to modify the behavior of the node.

For example, you can specify your own base asset id of the chain like below:

<<< @./snippets/launching-a-test-node.ts#custom-node-options{ts:line-numbers}

Note: The API for these options is still not fully complete and better documentation will come in the future.

providerOptions

Provider options passed on Provider instantiation. More on them here.

Fuel-Core Options

The launchTestNode creates a temporary snapshot directory and configurations every time it runs. The path to this directory is passed to fuel-core via the --snapshot flag.

Default Snapshot

The default snapshot used is that of the current testnet network iteration.

Click here to see what it looks like.

Custom Snapshot

If you need a different snapshot, you can specify a DEFAULT_CHAIN_SNAPSHOT_DIR environment variable which points to your snapshot directory. launchTestNode will read that config and work with it instead, integrating all the functionality with it the same way it'd do with the default config.

How and where you specify the environment variable depends on your testing tool.

<<< @./snippets/launching-a-test-node.ts#custom-chain-config{ts:line-numbers}

Fuel-Core Node Options

Besides the snapshot, you can provide arguments to the fuel-core node via the nodeOptions.args property. For a detailed list of all possible arguments run:

fuel-core run --help

If you want all your tests to run with the same arguments, consider specifying the DEFAULT_FUEL_CORE_ARGS environment variable.

<<< @./snippets/launching-a-test-node.ts#custom-fuel-core-args{ts:line-numbers}

Basic Example

Let's use launchTestNode with the counter contract from the Fuel dApp tutorial.

Note: you will have to change the import paths of the contract factory and bytecode to match your folder structure.

<<< @./snippets/launching-a-test-node.ts#basic-example{ts:line-numbers}

Summary

  1. The launched variable was instantiated with the using keyword.
  2. launchTestNode spun up a short-lived fuel-core node, deployed a contract to it and returned it for testing.
  3. The deployed contract is fully typesafe because of launchTestNode's type-level integration with typegen outputs.
  4. Besides the contract, you've got the provider and wallets at your disposal.

Advanced Example

A more complex example showcasing genesis block state configuration with walletsConfig and deployment of multiple contracts is shown below.

<<< @./snippets/launching-a-test-node.ts#advanced-example{ts:line-numbers}

Summary

  1. All points listed in the basic example apply here as well.
  2. Multiple wallets were generated with highly-specific coins and messages.
  3. It's possible to specify the wallet to be used for contract deployment via walletIndex.
  4. The test contract can be deployed with all the options available for real contract deployment.

Custom Blocks

You can force-produce blocks using the produceBlocks helper to achieve an arbitrary block height. This is especially useful when you want to do some testing regarding transaction maturity.

<<< @./snippets/tweaking-the-blockchain.ts#produce-blocks{ts:line-numbers}

Blocks With Custom Timestamps

You can also produce blocks with a custom block time using the produceBlocks helper by specifying the second optional parameter.

<<< @./snippets/tweaking-the-blockchain.ts#produceBlocks-custom-timestamp{ts:line-numbers}

Full Example

For a full example, see the following file: <<< @./snippets/tweaking-the-blockchain.ts#full{ts:line-numbers}

Setting up test wallets

You'll often want to create one or more test wallets when testing your contracts. Here's how to do it.

Create a single wallet

<<< @/../../docs/src/guide/wallets/snippets/access.ts#wallets{ts:line-numbers}

Setting up multiple test wallets

You can set up multiple test wallets using the launchTestNode utility via the walletsConfigs option.

To understand the different configurations, check out the walletsConfig in the test node options guide.

<<< @./snippets/launch-test-node-wallets.ts#multiple-wallets{ts:line-numbers}

Types

As you dive deeper into the SDK, it's essential to understand the variety of internal types available in both FuelVM and Sway, as well as their corresponding SDK equivalents. This section aims to provide you with the necessary knowledge to efficiently work with these types.

Overview

In this section, you will learn about:

  1. FuelVM and Sway Internal Types: Discover the various types used within FuelVM and Sway, and their significance in different contexts.

  2. SDK Equivalents: Explore the corresponding types available in the SDK, and understand their similarities and differences compared to FuelVM and Sway internal types.

  3. Type Usage: Gain insights into how to effectively use these types in your projects or applications, with examples and best practices.

  4. Type Conversion: Learn the techniques and methods for converting between FuelVM, Sway, and SDK types, guaranteeing smooth interoperability and consistent data integrity.

Additional Resources

As you progress through the documentation, you may find it helpful to refer back to the following resources:

  • Sway Documentation: Explore the Sway documentation homepage for an overview of Sway Types, as well as other sections.

  • The Fuel Book: A comprehensive guide to the whole Fuel ecosystem.

Address

In Sway, the Address type serves as a type-safe wrapper around the primitive B256 type. The SDK takes a different approach and has its own abstraction for the Address type.

Address Class

The Address class also provides a set of utility functions for easy manipulation and conversion between address formats along with one property; b256Address, which is of the B256 type.

<<< @/../../../packages/address/src/address.ts#address-2{ts:line-numbers}

Creating an Address

There are several ways to create an Address instance:

From a b256 address

To create an Address from a 256-bit address, use the following code snippet:

<<< @./snippets/address/from-a-b256.ts#full{ts:line-numbers}

From a Public Key

To create an Address from a public key, use the following code snippet:

<<< @./snippets/address/from-a-public-key.ts#full{ts:line-numbers}

From an EVM Address

To create an Address from an EVM address, use the following code snippet:

<<< @./snippets/address/from-an-evm-address.ts#full{ts:line-numbers}

From an existing Address

To create an Address from an existing Address instance, use the following code snippet:

<<< @./snippets/address/from-an-existing-address.ts#full{ts:line-numbers}

Utility functions

equals

As you may already notice, the equals function can compare addresses instances:

<<< @./snippets/address/utilities-function-equals.ts#full{ts:line-numbers}

toChecksum

To convert an address to a checksum address, use the toChecksum function:

<<< @./snippets/address/utilities-function-to-checksum.ts#full{ts:line-numbers}

Arrays

In Sway, an Array is a fixed-size collection of elements of the same type, similar to a Tuple. Arrays can hold arbitrary types, including non-primitive types, with their size determined at compile time.

Using Arrays in the SDK

You can pass a TypeScript Array into your contract method seamlessly just like you would pass an Array to a TypeScript function.

The SDK handles the conversion from TypeScript to Sway in the background, allowing the expected data to be passed through the type regardless of the Array type.

An Array in Sway is simply a typed Array, as demonstrated in the following example:

<<< @./snippets/arrays.ts#arrays-1{ts:line-numbers}

In Sway, Arrays are fixed in size, so the storage size is determined at the time of program compilation, not during runtime.

Let's say you have a contract that takes an Array of type u64 with a size length of 2 as a parameter and returns it:

<<< @/../../docs/sway/echo-values/src/main.sw#arrays-2{rust:line-numbers}

To execute the contract call using the SDK, you would do something like this:

<<< @./snippets/arrays.ts#arrays-3{ts:line-numbers}

You can easily access and validate the Array returned by the contract method, as demonstrated in the previous example.

As previously mentioned, Sway Arrays have a predefined type and size, so you need to be careful when passing Arrays as parameters to contract calls.

Passing an Array with an incorrect size, whether it has more or fewer elements than the specified length, will result in an error:

<<< @./snippets/arrays.ts#arrays-4{ts:line-numbers}

Similarly, passing an Array with an incorrect type will also result in an error:

<<< @./snippets/arrays.ts#arrays-5{ts:line-numbers}

Vectors

If your Array size is unknown until runtime, consider using the Vectors type, which is more suitable for dynamic-sized collections.

Asset ID

An Asset ID can be represented using the AssetId type. It's definition matches the Sway standard library type being a Struct wrapper around an inner B256 value.

<<< @./snippets/asset-id/intro.ts#full{ts:line-numbers}

Using an Asset ID

You can easily use the AssetId type within your Sway programs. Consider the following contract that can compares and return an AssetId:

<<< @/../../docs/sway/echo-asset-id/src/main.sw#asset-id-2{ts:line-numbers}

The AssetId struct can be passed to the contract function as follows:

<<< @./snippets/asset-id/using-an-asset-id-1.ts#snippet-1{ts:line-numbers}

And to validate the returned value:

<<< @./snippets/asset-id/using-an-asset-id-2.ts#snippet-1{ts:line-numbers}

B256

The type B256 in Fuel represents hashes and holds a 256-bit (32-bytes) value. The TypeScript SDK represents B256 as a hexlified string value for portability and provides utilities to convert to Uint8Array when the raw bytes are required.

Generating random B256 values

To generate a random B256 value, you can use the getRandomB256() function:

<<< @./snippets/b256/generating-random-b256.ts#full{ts:line-numbers}

Converting between B256 and Uint8Array

To convert between a B256 hexlified string and a Uint8Array, you can use the arrayify and hexlify functions:

<<< @./snippets/b256/converting-between-b256-uint8.ts#full{ts:line-numbers}

Support from Address Class

A B256 value is also supported as part of the Address class, providing seamless integration with other components of your application. To create an Address instance from a b256 value, use the new Address() method:

<<< @./snippets/b256/support-from-address-class.ts#full{ts:line-numbers}

B512

In Sway, the B512 type is commonly used to handle public keys and signatures. This guide will explain how the B512 type is defined in Sway, how to visualize a B512 value using the SDK, and how to interact with a contract function that accepts a B512 parameter.

The B512 type in Sway is a wrapper around two B256 types, allowing for the representation of 64-byte values. It is defined as a struct:

<<< @/../../docs/sway/bytecode-input/src/main.sw#b512-1{rs:line-numbers}

B512 in the SDK

In the SDK, you can visualize a B512 value by examining a wallet's public key:

<<< @./snippets/b512/b512-in-the-sdk.ts#snippet-1{ts:line-numbers}

Example: Echoing a B512 Value in a Contract Function

Let's consider a contract function that accepts a B512 parameter and returns the same value:

<<< @/../../docs/sway/echo-values/src/main.sw#b512-3{rust:line-numbers}

To call this function and validate the returned value, follow these steps:

<<< @./snippets/b512/echoing-a-b512.ts#snippet-1{ts:line-numbers}

In this example, we generate a wallet, use its public key as the B512 input, call the echo_b512 contract function, and expect the returned value to match the original input.

Bytes

A dynamic array of byte values can be represented using the Bytes type, which represents raw bytes.

Using Bytes

The Bytes type can be integrated with your contract calls. Consider the following contract that can compare and return a Bytes:

<<< @/../../docs/sway/echo-bytes/src/main.sw#bytes-1{ts:line-numbers}

A Bytes array can be created using a native JavaScript array of numbers or Big Numbers, and sent to a Sway contract:

<<< @./snippets/bytes.ts#snippet-1{ts:line-numbers}

Bytes32

In Sway and the FuelVM, bytes32 is used to represent hashes. It holds a 256-bit (32-bytes) value.

Generating Random bytes32 Values

To generate a random bytes32 value, you can use the randomBytes function from the fuels module:

<<< @./snippets/bytes32/generating-random-bytes32.ts#snippet-1{ts:line-numbers}

Converting Between Byte Arrays and Strings

You can use the hexlify function to convert a byte array to a hex string, and the arrayify function to convert a hex string back to a byte array:

<<< @./snippets/bytes32/converting-between-byte.ts#snippet-1{ts:line-numbers}

Working with b256 in Fuel

In Fuel, there is a special type called b256, which is similar to bytes32. Like bytes32, B256 is also used to represent hashes and holds a 256-bit value. You can learn more about working with B256 values in the B256 documentation.

Enums

Sway Enums are a little distinct from TypeScript Enums. In this document, we will explore how you can represent Sway Enums in the SDK and how to use them with Sway contract functions.

Basic Sway Enum Example

Consider the following basic Sway Enum called StateError:

<<< @/../../docs/sway/echo-enum/src/main.sw#enums-1{rust:line-numbers}

The type () indicates that there is no additional data associated with each Enum variant. Sway allows you to create Enums of Enums or associate types with Enum variants.

Using Sway Enums As Function Parameters

Let's define a Sway contract function that takes a StateError Enum variant as an argument and returns it:

<<< @/../../docs/sway/echo-enum/src/main.sw#enums-2{rust:line-numbers}

To execute the contract function and validate the response, we can use the following code:

<<< @./snippets/enums/using-sway-enums.ts#snippet-1{ts:line-numbers}

In this example, we simply pass the Enum variant as a value to execute the contract function call.

Enum of Enums Example

In this example, the Error Enum is an Enum of two other Enums: StateError and UserError.

<<< @/../../docs/sway/echo-enum/src/main.sw#enums-4{rust:line-numbers}

Using Enums of Enums with Contract Functions

Now, let's create a Sway contract function that accepts any variant of the Error Enum as a parameter and returns it immediately. This variant could be from either the StateError or UserError Enums.

<<< @/../../docs/sway/echo-enum/src/main.sw#enums-5{rust:line-numbers}

Since the Error Enum is an Enum of Enums, we need to pass the function parameter differently. The parameter will be a TypeScript object:

<<< @./snippets/enums/using-enums-of-enums-1.ts#snippet-1{ts:line-numbers}

In this case, since the variant InsufficientPermissions belongs to the UserError Enum, we create a TypeScript object using the Enum name as the object key and the variant as the object value.

We would follow the same approach if we intended to use a variant from the StateError Enum:

<<< @./snippets/enums/using-enums-of-enums-2.ts#snippet-1{ts:line-numbers}

Errors

While working with enums, you may run into the following issues:

Using an invalid enum type

Thrown when the type being passed to the enum does not match that expected by it.

<<< @./snippets/enums/using-an-invalid-enum-type.ts#snippet-1{ts:line-numbers}

Using an invalid enum value

Thrown when the parameter passed is not an expected enum value.

<<< @./snippets/enums/using-an-invalid-enum-value.ts#snippet-1{ts:line-numbers}

Using an invalid enum case key

Thrown when the passed enum case is not an expected enum case value.

<<< @./snippets/enums/using-an-invalid-enum-case.ts#snippet-1{ts:line-numbers}

EvmAddress

An Ethereum Virtual Machine (EVM) Address can be represented using the EvmAddress type. It's definition matches the Sway standard library type being a Struct wrapper around an inner B256 value.

<<< @./snippets/evm-address/intro.ts#snippet-1{ts:line-numbers}

Creating an EVM Address

An EVM Address only has 20 bytes therefore the first 12 bytes of the B256 value are set to 0. Within the SDK, an Address can be instantiated and converted to a wrapped and Sway compatible EVM Address using the toEvmAddress() function:

<<< @./snippets/evm-address/creating-an-evm.ts#snippet-1{ts:line-numbers}

Using an EVM Address

The EvmAddress type can be integrated with your contract calls. Consider the following contract that can compare and return an EVM Address:

<<< @/../../docs/sway/echo-evm-address/src/main.sw#evm-address-1{ts:line-numbers}

The EvmAddress type can be used with the SDK and passed to the contract function as follows:

<<< @./snippets/evm-address/using-an-evm-address-1.ts#snippet-1{ts:line-numbers}

And to validate the returned value:

<<< @./snippets/evm-address/using-an-evm-address-2.ts#snippet-1{ts:line-numbers}

Native Parameter Types

Below you can find examples of how to convert between common native Sway program input and output types:

Address

AddressInput

To pass an Address as an input parameter to a Sway program, you can define the input as shown below:

<<< @./snippets/native-parameters/address.ts#address-input

AddressOutput

For a Sway program that returns an Address type, you can convert the returned value to an Address type in fuels as shown below:

<<< @./snippets/native-parameters/address.ts#address-output

ContractId

ContractIdInput

To pass a ContractId as an input parameter to a Sway program, you can define the input as shown below:

<<< @./snippets/native-parameters/contract-id.ts#contract-id-input

ContractIdOutput

For a Sway program that returns a ContractId type, you can convert the returned value to a string as shown below:

<<< @./snippets/native-parameters/contract-id.ts#contract-id-output

Identity

IdentityInput

To pass an Identity as an input parameter to a Sway program, you can define the input as shown below:

For an address:

<<< @./snippets/native-parameters/identity-address.ts#identity-address-input

For a contract:

<<< @./snippets/native-parameters/identity-contract.ts#identity-contract-input

IdentityOutput

For a Sway program that returns an Identity type, you can convert the returned value to an Address or string as shown below:

For an address:

<<< @./snippets/native-parameters/identity-address.ts#identity-address-output

For a contract:

<<< @./snippets/native-parameters/identity-contract.ts#identity-contract-output

AssetId

AssetIdInput

To pass an AssetId as an input parameter to a Sway program, you can define the input as shown below:

<<< @./snippets/native-parameters/asset-id.ts#asset-id-input

AssetIdOutput

For a Sway program that returns an AssetId type, you can convert the returned value to a string as shown below:

<<< @./snippets/native-parameters/asset-id.ts#asset-id-output

Numbers

In Sway, there are multiple primitive number types:

  1. u8 (8-bit unsigned integer)
  2. u16 (16-bit unsigned integer)
  3. u32 (32-bit unsigned integer)
  4. u64 (64-bit unsigned integer)
  5. u256 (256-bit unsigned integer)

This guide explains how to create and interact with Sway numbers while using the SDK.

Creating Numbers

For u64 and u256

When you pass in a u64 or a u256 to a Sway program from JavaScript, you must first convert it to a BigNum object. This is because these types can have extremely large maximum values (2^64 and 2^256 respectively), and JavaScript's Number type can only hold up to 53 bits of precision (2^53).

<<< @./snippets/numbers/for-u64-and-u256-1.ts#snippet-1{ts:line-numbers}

You can also create a BigNum from a string. This is useful when you want to pass in a number that is too large to be represented as a JavaScript number. Here's how you can do that:

<<< @./snippets/numbers/for-u64-and-u256-2.ts#snippet-1{ts:line-numbers}

For u8, u16, and u32

You don't need to do anything special to create these numbers. You can pass in a JavaScript number directly. See the examples below for more details.

Examples: Interacting with Numbers in Contract Methods

For u64 and u256

<<< @./snippets/numbers/for-u64-and-u256-3.ts#snippet-1{ts:line-numbers}

Note: If a contract call returns a number that is too large to be represented as a JavaScript number, you can convert it to a string using the .toString() method instead of .toNumber().

For u8, u16, and u32

<<< @./snippets/numbers/for-u8-u16-and-u32-1.ts#snippet-1{ts:line-numbers}

Using a BigNum from ethers with fuels

<<< @./snippets/numbers/for-u8-u16-and-u32-2.ts#snippet-1{ts:line-numbers}

Options

Sway provides the Option (optional) container for handling variables that can have a value or be marked as no-value. This concept is useful when dealing with situations where a variable may or may not have a defined value.

In this guide, we'll explain how to work with Option types in Sway and demonstrate their usage through a practical example.

Overview of Option Type

The Option type in Sway is a special wrapper type of Enum. In TypeScript, you can represent the Option type by using the undefined keyword, as shown in the following example

<<< @./snippets/options/overview-of-option.ts#snippet-1{ts:line-numbers}

In this example, the variable input1 can be either a number or undefined.

Example: Option<u8> Parameters

Let's say we have a contract function that accepts two Option<u8> parameters. Both of these parameters can have a value or be undefined. The function checks whether each input has a value; if not, it assigns a value of 0. Finally, the function returns the sum of the two inputs.

Here's the contract function written in Sway:

<<< @/../../docs/sway/sum-option-u8/src/main.sw#options-2{rust:line-numbers}

You can interact with the contract function using the SDK as follows:

<<< @./snippets/options/overview-of-option.ts#snippet-2{ts:line-numbers}

In this case, the result of the contract function call is the sum of both input parameters. If we pass only one parameter, the contract function will default the other parameter's value to 0.

<<< @./snippets/options/example-option-u8.ts#snippet-1{ts:line-numbers}

Using Option types in Sway allows you to elegantly handle situations where a variable may or may not have a defined value.

RawSlice

A dynamic array of values can be represented using the RawSlice type. A raw slice can be a value reference or a raw pointer.

Using a RawSlice

The RawSlice type can be integrated with your contract calls. Consider the following contract that can compare and return a RawSlice:

<<< @/../../docs/sway/echo-raw-slice/src/main.sw#raw-slice-1{ts:line-numbers}

A RawSlice can be created using a native JavaScript array of numbers or Big Numbers, and sent to a Sway contract:

<<< @./snippets/raw-slice.ts#raw-slice-2{ts:line-numbers}

StdString

A dynamic string of variable length can be represented using the StdString type, also known as a Standard Lib String or std-lib-string. It behaves much like a dynamic string in most languages, and is essentially an array of characters.

Using a StdString

The StdString type can be integrated with your contract calls. Consider the following contract that can compare and return a String:

<<< @/../../docs/sway/echo-std-string/src/main.sw#std-string-1{ts:line-numbers}

A string can be created using a native JavaScript string, and sent to a Sway contract:

<<< @./snippets/std-string.ts#std-string-2{ts:line-numbers}

String

In Sway, strings are statically-sized, which means you must define the size of the string beforehand. Statically-sized strings are represented using the str[x] syntax, where x indicates the string's size. This guide explains how to create and interact with statically-sized strings while using the SDK.

Creating Statically-Sized Strings

<<< @./snippets/string.ts#string-1{ts:line-numbers}

Interacting with Statically-Sized Strings in Contract Methods

When a contract method accepts and returns a str[8], the corresponding SDK wrapper method will also take and return a string of the same length. You can pass a string to the contract method like this:

<<< @./snippets/string.ts#string-2{ts:line-numbers}

When working with statically-sized strings, ensure that the input and output strings have the correct length to avoid erroneous behavior.

If you pass a string that is either too long or too short for a contract method, the call will fail like this:

<<< @./snippets/string.ts#string-3{ts:line-numbers}

Structs

In Sway, a struct serves a similar purpose as an Object in TypeScript. It defines a custom data structure with specified property names and types. The property names and types in the Sway struct must match the corresponding TypeScript definition.

Example

Here is an example of a struct in Sway:

<<< @/../../docs/sway/employee-data/src/lib.sw#struct-1{rust:line-numbers}

And here is the equivalent structure represented in TypeScript:

<<< @./snippets/structs.ts#struct-2{ts:line-numbers}

Handling Different Data Types

Please note that TypeScript does not have native support for u8 and u64 types. Instead, use the number type to represent them.

Additionally, TypeScript does not support specifying string length, so just use string for the name.

In a similar way, since the type B256 on the SDK is just an hexlified string, we use string as well.

Tuples

In Sway, Tuples are fixed-length collections of heterogeneous elements. Tuples can store multiple data types, including basic types, structs, and enums. This guide will demonstrate how to represent and work with Tuples in TypeScript and interact with a contract function that accepts a tuple as a parameter.

In TypeScript, you can represent Sway tuples using arrays with specified types for each element:

<<< @./snippets/tuples.ts#tuples-1{ts:line-numbers}

In this example, the Typescript tuple variable contains three elements of different types: a number, a boolean, and another number.

Example: Passing Tuple as a Parameter

Let's consider a contract function that accepts a tuple as a parameter and returns the same Tuple:

<<< @/../../docs/sway/echo-values/src/main.sw#tuples-2{rust:line-numbers}

To execute and validate the contract function using the SDK, follow these steps:

<<< @./snippets/tuples.ts#tuples-3{ts:line-numbers}

In this example, we create a Tuple with three elements, call the echo_tuple contract function, and expect the returned tuple to match the original one. Note that we convert the third element of the returned tuple to a number using new BN(value[2]).toNumber().

Tuples in Sway provide a convenient way to store and manipulate collections of heterogeneous elements. Understanding how to represent and work with tuples in TypeScript and Sway contracts will enable you to create more versatile and expressive code.

Vectors

In Sway, a Vector is a dynamic-sized collection of elements of the same type. Vectors can hold arbitrary types, including non-primitive types.

Working with Vectors in the SDK

A basic Vector in Sway is similar to a TypeScript Array:

<<< @./snippets/vectors.ts#vector-1{ts:line-numbers}

Consider the following example of a EmployeeData struct in Sway:

<<< @/../../docs/sway/employee-data/src/lib.sw#struct-1{rust:line-numbers}

Now, let's look at the following contract method. It receives a Vector of the Transaction struct type as a parameter and returns the last Transaction entry from the Vector:

<<< @/../../docs/sway/echo-employee-data-vector/src/main.sw#vector-3{ts:line-numbers}

The code snippet below demonstrates how to call this Sway contract method, which accepts a Vec<Transaction>:

<<< @./snippets/vectors.ts#vector-4{ts:line-numbers}

Converting Bytecode to Vectors

Some functions require you to pass in bytecode to the function. The type of the bytecode parameter is usually Vec<u8>, here's an example of how to pass bytecode to a function:

<<< @/../../docs/sway/bytecode-input/src/main.sw#vector-bytecode-input-sway{ts:line-numbers}

To pass bytecode to this function, you can make use of the arrayify function to convert the bytecode file contents into a UInt8Array, the TS compatible type for Sway's Vec<u8> type and pass it the function like so:

<<< @./snippets/vectors.ts#vector-bytecode-input-ts{ts:line-numbers}

Errors

All errors thrown from the SDK are instances of the FuelError class which will have an accompanying ErrorCode.

Error Codes

Here is a list of the expected error codes the SDK can throw. These error codes are used to help understand the error that has been thrown with potential resolutions.

ABI_MAIN_METHOD_MISSING

When your ABI does not have a main method.

This can be resolved by adding a main method to your ABI. This is prevalent in scripts and predicates that must contain a main method.

ABI_TYPES_AND_VALUES_MISMATCH

When the arguments supplied to the function do not match the minimum required input length.

Check that the arguments supplied to the function match the required type.

ACCOUNT_REQUIRED

When an Account is required for an operation. This will usually be in the form of a Wallet.

It could be caused during the deployments of contracts when an account is required to sign the transaction. This can be resolved by following the deployment guide here.

ASSET_BURN_DETECTED

When you are trying to send a transaction that will result in an asset burn.

Add relevant coin change outputs to the transaction, or enable asset burn in the transaction request.

CONFIG_FILE_NOT_FOUND

When a configuration file is not found. This could either be a fuels.config.[ts,js,mjs,cjs] file or a TOML file.

Ensure that the configuration file is present in the root directory of your project.

CONFIG_FILE_ALREADY_EXISTS

When a configuration file already exists in the root directory of your project.

You can not run fuels init more than once for a given project. Either remove the existing configuration file or update it.

CONVERTING_FAILED

When converting a big number into an incompatible format.

Ensure that the value you've supplied to the big number is compatible with the value you are converting to.

CONTRACT_SIZE_EXCEEDS_LIMIT

When the contract size exceeds the maximum contract size limit.

Ensure that the contract size is less than the maximum contract size limit, of 100 KB. This can be validated by checking the bytecode length of the contract.

DUPLICATED_POLICY

When there are more than policies with the same type, for a transaction.

Ensure that there are no duplicate (by type) policies for a transaction.

ERROR_BUILDING_BLOCK_EXPLORER_URL

When more than one of the following options is passed: path, address, txId, blockNumber.

Check that only one of the above is passed.

FUNCTION_NOT_FOUND

When the function with the given name, signature or selector is not found in the ABI.

Check that the function name, signature or selector is correct and exits on the ABI.

FUNDS_TOO_LOW

When the funds in the account are lower than the required amount.

Ensure that the account has enough funds to cover the transaction.

GAS_LIMIT_TOO_LOW

When the gas limit is lower than the minimum gas limit.

Increase the gas limit to be greater than the minimum gas limit.

GAS_PRICE_TOO_LOW

When the gas price is lower than the minimum gas price.

Increase the gas price to be greater than the minimum gas price.

HD_WALLET_ERROR

A hardware wallet will throw for unsupported configurations.

The error message will determine which element of the configuration is incorrect. It could be due to the public or private key or when configuring to/from an extended key.

INVALID_CHECKSUM

Checksum validation failed for the provided mnemonic.

Ensure that the mnemonic is correct.

INVALID_CHUNK_SIZE_MULTIPLIER

When the chunk size multiplier is not between 0 and 1.

Ensure that the chunk size multiplier is a number that it is between 0 and 1.

INVALID_CONFIGURABLE_CONSTANTS

When the program type either: does not have configurable constants to be set; or the provided configurable constant does not belong to the program type, as defined by its ABI.

Ensure the configurable constants provided are correct and are defined in ABI.

INVALID_COMPONENT

When an expected component is not found in the ABI or is malformed.

Ensure that you have correctly formed Sway types for Arrays and Vectors.

INVALID_CREDENTIALS

When the password provided is incorrect.

Ensure that the password is correct.

INVALID_DATA

When the value being passed is not considered valid, as defined by the function.

Check the function signature and ensure that the passed value is valid.

INVALID_ENTROPY

When the entropy is not: between 16 and 32 bytes; a multiple of 4.

Ensure that the entropy is between 16 and 32 bytes and a multiple of 4.

INVALID_EVM_ADDRESS

When the provided EVM address is invalid.

Ensure that the EVM address is valid.

INVALID_INPUT_PARAMETERS

When the provided input parameters are not valid.

The error message will determine which parameter is missing. It could be that the provided program type is not one of the following contract, script, or predicate.

INVALID_MNEMONIC

When the supplied mnemonic is invalid.

Check the message for more details. It could be that the mnemonic phrase word length is not one of the following: 12, 15, 18, 21, or 24 lengths.

INVALID_PASSWORD

When the provided password is incorrect.

Ensure that the password is correct.

INVALID_POLICY_TYPE

When the supplied policy type is invalid for the given Script.

Check the policy type is defined in PolicyType.

INVALID_PROVIDER

When unable to connect to the Provider or Network supplied to a method on the Fuel class.

Check that the Provider or Network is supplied correctly.

INVALID_PUBLIC_KEY

When the provided public key is invalid.

Ensure that the public key is valid.

INVALID_RECEIPT_TYPE

When the receipt type is invalid.

Check the type is within ReceiptType.

INVALID_REQUEST

When the request to the Fuel node fails, error messages are propagated from the Fuel node.

Check the error message from the Fuel node.

INVALID_SEED

When the seed length is not between 16 and 64 bytes.

Ensure that the seed length is between 16 and 64 bytes.

INVALID_TRANSACTION_INPUT

When the input type is invalid.

Check the type is within InputType.

INVALID_TRANSACTION_OUTPUT

When the output type is invalid.

Check the type is within OutputType.

INVALID_TRANSACTION_STATUS

When the transaction status received from the node is unexpected.

Check the status received is within TransactionStatus.

UNSUPPORTED_TRANSACTION_TYPE

When the transaction type from the Fuel Node is not supported.

The type is within TransactionType.

INVALID_TTL

When the TTL is less than or equal to zero.

Ensure that the TTL is a number and that the TTL is greater than zero.

INVALID_WORD_LIST

When the word list length is not equal to 2048.

The word list provided to the mnemonic length should be equal to 2048.

INVALID_URL

When the URL provided is invalid.

Ensure that the URL is valid.

JSON_ABI_ERROR

When an ABI type does not conform to the correct format.

It is usually caused by an incorrect type/s within your program, check our type docs here for information on the types we support and their expected format.

LOG_TYPE_NOT_FOUND

When the log type ID supplied can not be found in the ABI.

Check that the log type ID is correct and exists in the ABI.

MISSING_CONNECTOR

A connector is missing when it's required for a given operation.

Ensure that a connector has been supplied to the Account or Wallet.

MISSING_PROVIDER

A provider is missing when it's required for a given operation.

It could be caused by the provider not being set for either an Account or a Wallet - use the connect method to attach a provider.

MISSING_REQUIRED_PARAMETER

When a required parameter has not been supplied to a given method.

The error message will determine which parameter is missing. This could be caused during type generation when neither inputs nor filepaths are supplied (at least one is required).

NODE_INFO_CACHE_EMPTY

When the Fuel Node info cache is empty; This is usually caused by not being connected to the Fuel Node.

Ensure that the provider has connected to a Fuel Node successfully.

INSUFFICIENT_FUNDS_OR_MAX_COINS

This error can occur during a funding operation or when calling the getResourcesToSpend method. It indicates one of the following issues:

Insufficient Balance: The specified account does not have enough balance to cover the required amount.

UTXO Limit Exceeded: Although the account has enough total funds, the funds are spread across too many UTXOs (coins). The blockchain limits how many UTXOs can be used in a single transaction, and exceeding this limit prevents the transaction from being processed.

First, to be sure what the real reason is, you can fetch the balance of the assetId to ensure that the account has enough funds to cover the amount. After knowing the reason, to solve you can:

For Insufficient Balance: Acquire additional funds in the required asset to meet the amount needed.

For UTXO Limit Exceeded: Combine UTXOs to reduce their number and meet the network's requirements. You can follow this guide to learn how to combine UTXOs effectively.

TIMEOUT_EXCEEDED

When the timeout has been exceeded for a given operation.

Check that you're connected to the network and that the network is stable.

TYPE_NOT_FOUND

When the type with the given type ID is not found in the ABI.

Check that the type ID is correct and exists in the ABI.

TYPE_NOT_SUPPORTED

When an unexpected type has been detected - the error message will determine which type is incorrect.

Check the type against your ABI and ensure that it is correct. You can find a list of all our types here.

UNSUPPORTED_FUEL_CLIENT_VERSION

When the version of the Fuel Node you are targeting is not supported by the client you are accessing it from.

Check the version of the Fuel Node and use a compatible version of the SDK to target it.

WALLET_MANAGER_ERROR

A wallet manager will throw for a multitude of reasons. The error message will determine which element of the configuration is incorrect.

It could be that the passphrase is incorrect and/or the wallet does not exist in the manager.

WORKSPACE_NOT_DETECTED

When the workspace is not detected in the directory indicated in the message.

Ensure that the workspace is present in the directory specified.

UNKNOWN

In cases where the error hasn't been mapped yet, this code will be used.

If you believe you found a bug, please report the issue to the team.

MAX_INPUTS_EXCEEDED

When the number of transaction inputs exceeds the maximum limit allowed by the blockchain.

MAX_OUTPUTS_EXCEEDED

When the number of transaction outputs exceeds the maximum limit allowed by the blockchain.